From b182f3c481051bb39080db67693dc1ee0f9c99bc Mon Sep 17 00:00:00 2001 From: Yvan Duhamel Date: Mon, 8 Jul 2019 15:19:06 +0200 Subject: [PATCH 01/11] Add method LDAPCPConfig.CreateDefaultConfiguration --- CHANGELOG.md | 4 +++ LDAPCP.Tests/BackupCurrentConfig.cs | 2 +- LDAPCP.Tests/UnitTestsHelper.cs | 2 +- .../Features/LDAPCP/LDAPCP.EventReceiver.cs | 2 +- LDAPCP/LDAPCPConfig.cs | 26 ++++++++++++++++++- 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6064c7e..7d155c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change log for LDAPCP +## Unreleased + +* Add method LDAPCPConfig.CreateDefaultConfiguration + ## LDAPCP 13.0.20190621.905 enhancements & bug-fixes - Published in June 21, 2019 * Add a default mapping to populate the email of groups diff --git a/LDAPCP.Tests/BackupCurrentConfig.cs b/LDAPCP.Tests/BackupCurrentConfig.cs index d529242..1f9aa96 100644 --- a/LDAPCP.Tests/BackupCurrentConfig.cs +++ b/LDAPCP.Tests/BackupCurrentConfig.cs @@ -20,7 +20,7 @@ public void Init() if (Config == null) { Trace.TraceWarning($"{DateTime.Now.ToString("s")} Configuration {UnitTestsHelper.ClaimsProviderConfigName} does not exist, create it with default settings..."); - Config = LDAPCPConfig.CreateConfiguration(ClaimsProviderConstants.CONFIG_ID, ClaimsProviderConstants.CONFIG_NAME, UnitTestsHelper.SPTrust.Name); + Config = LDAPCPConfig.CreateDefaultConfiguration(UnitTestsHelper.SPTrust.Name); } BackupConfig = Config.CopyConfiguration(); InitializeConfiguration(); diff --git a/LDAPCP.Tests/UnitTestsHelper.cs b/LDAPCP.Tests/UnitTestsHelper.cs index 42b9c1b..428a042 100644 --- a/LDAPCP.Tests/UnitTestsHelper.cs +++ b/LDAPCP.Tests/UnitTestsHelper.cs @@ -74,7 +74,7 @@ public static void InitializeSiteCollection() LDAPCPConfig config = LDAPCPConfig.GetConfiguration(UnitTestsHelper.ClaimsProviderConfigName, UnitTestsHelper.SPTrust.Name); if (config == null) { - LDAPCPConfig.CreateConfiguration(ClaimsProviderConstants.CONFIG_ID, ClaimsProviderConstants.CONFIG_NAME, SPTrust.Name); + LDAPCPConfig.CreateDefaultConfiguration(UnitTestsHelper.SPTrust.Name); } var service = SPFarm.Local.Services.GetValue(String.Empty); diff --git a/LDAPCP/Features/LDAPCP/LDAPCP.EventReceiver.cs b/LDAPCP/Features/LDAPCP/LDAPCP.EventReceiver.cs index 15dee17..82e020d 100644 --- a/LDAPCP/Features/LDAPCP/LDAPCP.EventReceiver.cs +++ b/LDAPCP/Features/LDAPCP/LDAPCP.EventReceiver.cs @@ -46,7 +46,7 @@ private void ExecBaseFeatureActivated(SPFeatureReceiverProperties properties) { LDAPCPConfig existingConfig = LDAPCPConfig.GetConfiguration(ClaimsProviderConstants.CONFIG_NAME); if (existingConfig == null) - LDAPCPConfig.CreateConfiguration(ClaimsProviderConstants.CONFIG_ID, ClaimsProviderConstants.CONFIG_NAME, spTrust.Name); + LDAPCPConfig.CreateDefaultConfiguration(spTrust.Name); else ClaimsProviderLogging.Log($"[{LDAPCP._ProviderInternalName}] Use configuration \"{ClaimsProviderConstants.CONFIG_NAME}\" found in the configuration database", TraceSeverity.High, EventSeverity.Information, ClaimsProviderLogging.TraceCategory.Configuration); } diff --git a/LDAPCP/LDAPCPConfig.cs b/LDAPCP/LDAPCPConfig.cs index 887372b..171686a 100644 --- a/LDAPCP/LDAPCPConfig.cs +++ b/LDAPCP/LDAPCPConfig.cs @@ -481,17 +481,41 @@ public void ResetClaimTypesList() TraceSeverity.High, EventSeverity.Information, TraceCategory.Core); } + /// + /// Create LDAPCP configuration with default settings, and save it into configuration database. If it already exists, it will be deleted. + /// + /// Name of the SPTrustedLoginProvider that LDAPCP is associated with + /// + public static LDAPCPConfig CreateDefaultConfiguration(string spTrustName) + { + if (String.IsNullOrEmpty(spTrustName)) + { + throw new ArgumentNullException("spTrustName"); + } + + SPTrustedLoginProvider spTrust = LDAPCP.GetSPTrustAssociatedWithCP(LDAPCP._ProviderInternalName); + if (spTrust == null) + { + return null; + } + else + { + return CreateConfiguration(ClaimsProviderConstants.CONFIG_ID, ClaimsProviderConstants.CONFIG_NAME, spTrust.Name); + } + } + /// /// Create a persisted object with default configuration of LDAPCP. If it already exists, it will be deleted. /// /// GUID of persisted object /// Name of persisted object + /// Name of the SPTrustedLoginProvider that LDAPCP is associated with /// public static LDAPCPConfig CreateConfiguration(string persistedObjectID, string persistedObjectName, string spTrustName) { if (String.IsNullOrEmpty(spTrustName)) { - throw new ArgumentNullException("spTrust"); + throw new ArgumentNullException("spTrustName"); } // Ensure it doesn't already exists and delete it if so From 596c3762b52afa12ebe05d7b4366b70c9dc0f40d Mon Sep 17 00:00:00 2001 From: Yvan Duhamel Date: Mon, 8 Jul 2019 16:13:40 +0200 Subject: [PATCH 02/11] Update method LDAPCPConfig.CreateDefaultConfiguration --- LDAPCP.Tests/BackupCurrentConfig.cs | 2 +- LDAPCP.Tests/UnitTestsHelper.cs | 4 ++-- LDAPCP/Features/LDAPCP/LDAPCP.EventReceiver.cs | 15 +++++++-------- LDAPCP/LDAPCPConfig.cs | 10 ++-------- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/LDAPCP.Tests/BackupCurrentConfig.cs b/LDAPCP.Tests/BackupCurrentConfig.cs index 1f9aa96..d529242 100644 --- a/LDAPCP.Tests/BackupCurrentConfig.cs +++ b/LDAPCP.Tests/BackupCurrentConfig.cs @@ -20,7 +20,7 @@ public void Init() if (Config == null) { Trace.TraceWarning($"{DateTime.Now.ToString("s")} Configuration {UnitTestsHelper.ClaimsProviderConfigName} does not exist, create it with default settings..."); - Config = LDAPCPConfig.CreateDefaultConfiguration(UnitTestsHelper.SPTrust.Name); + Config = LDAPCPConfig.CreateConfiguration(ClaimsProviderConstants.CONFIG_ID, ClaimsProviderConstants.CONFIG_NAME, UnitTestsHelper.SPTrust.Name); } BackupConfig = Config.CopyConfiguration(); InitializeConfiguration(); diff --git a/LDAPCP.Tests/UnitTestsHelper.cs b/LDAPCP.Tests/UnitTestsHelper.cs index 428a042..831bf1c 100644 --- a/LDAPCP.Tests/UnitTestsHelper.cs +++ b/LDAPCP.Tests/UnitTestsHelper.cs @@ -17,8 +17,8 @@ [SetUpFixture] public class UnitTestsHelper { - public static readonly ldapcp.LDAPCP ClaimsProvider = new ldapcp.LDAPCP(UnitTestsHelper.ClaimsProviderName); public static string ClaimsProviderName => "LDAPCP"; + public static readonly ldapcp.LDAPCP ClaimsProvider = new ldapcp.LDAPCP(UnitTestsHelper.ClaimsProviderName); public static readonly string ClaimsProviderConfigName = TestContext.Parameters["ClaimsProviderConfigName"]; public static Uri TestSiteCollUri; public static readonly string TestSiteRelativePath = $"/sites/{TestContext.Parameters["TestSiteCollectionName"]}"; @@ -74,7 +74,7 @@ public static void InitializeSiteCollection() LDAPCPConfig config = LDAPCPConfig.GetConfiguration(UnitTestsHelper.ClaimsProviderConfigName, UnitTestsHelper.SPTrust.Name); if (config == null) { - LDAPCPConfig.CreateDefaultConfiguration(UnitTestsHelper.SPTrust.Name); + LDAPCPConfig.CreateConfiguration(ClaimsProviderConstants.CONFIG_ID, ClaimsProviderConstants.CONFIG_NAME, SPTrust.Name); } var service = SPFarm.Local.Services.GetValue(String.Empty); diff --git a/LDAPCP/Features/LDAPCP/LDAPCP.EventReceiver.cs b/LDAPCP/Features/LDAPCP/LDAPCP.EventReceiver.cs index 82e020d..dee0dd1 100644 --- a/LDAPCP/Features/LDAPCP/LDAPCP.EventReceiver.cs +++ b/LDAPCP/Features/LDAPCP/LDAPCP.EventReceiver.cs @@ -40,15 +40,14 @@ private void ExecBaseFeatureActivated(SPFeatureReceiverProperties properties) { ClaimsProviderLogging svc = ClaimsProviderLogging.Local; ClaimsProviderLogging.Log($"[{LDAPCP._ProviderInternalName}] Activating farm-scoped feature for claims provider \"{LDAPCP._ProviderInternalName}\"", TraceSeverity.High, EventSeverity.Information, ClaimsProviderLogging.TraceCategory.Configuration); - - var spTrust = LDAPCP.GetSPTrustAssociatedWithCP(LDAPCP._ProviderInternalName); - if (spTrust != null) + LDAPCPConfig existingConfig = LDAPCPConfig.GetConfiguration(ClaimsProviderConstants.CONFIG_NAME); + if (existingConfig == null) + { + LDAPCPConfig.CreateDefaultConfiguration(); + } + else { - LDAPCPConfig existingConfig = LDAPCPConfig.GetConfiguration(ClaimsProviderConstants.CONFIG_NAME); - if (existingConfig == null) - LDAPCPConfig.CreateDefaultConfiguration(spTrust.Name); - else - ClaimsProviderLogging.Log($"[{LDAPCP._ProviderInternalName}] Use configuration \"{ClaimsProviderConstants.CONFIG_NAME}\" found in the configuration database", TraceSeverity.High, EventSeverity.Information, ClaimsProviderLogging.TraceCategory.Configuration); + ClaimsProviderLogging.Log($"[{LDAPCP._ProviderInternalName}] Use configuration \"{existingConfig.Name}\" found in the configuration database", TraceSeverity.High, EventSeverity.Information, ClaimsProviderLogging.TraceCategory.Configuration); } } catch (Exception ex) diff --git a/LDAPCP/LDAPCPConfig.cs b/LDAPCP/LDAPCPConfig.cs index 171686a..469e821 100644 --- a/LDAPCP/LDAPCPConfig.cs +++ b/LDAPCP/LDAPCPConfig.cs @@ -482,17 +482,11 @@ public void ResetClaimTypesList() } /// - /// Create LDAPCP configuration with default settings, and save it into configuration database. If it already exists, it will be deleted. + /// If LDAPCP is associated with a SPTrustedLoginProvider, create its configuration with default settings and save it into configuration database. If it already exists, it will be replaced. /// - /// Name of the SPTrustedLoginProvider that LDAPCP is associated with /// - public static LDAPCPConfig CreateDefaultConfiguration(string spTrustName) + public static LDAPCPConfig CreateDefaultConfiguration() { - if (String.IsNullOrEmpty(spTrustName)) - { - throw new ArgumentNullException("spTrustName"); - } - SPTrustedLoginProvider spTrust = LDAPCP.GetSPTrustAssociatedWithCP(LDAPCP._ProviderInternalName); if (spTrust == null) { From 024cd321378301f349d8b7eb69bc3175acf64c3a Mon Sep 17 00:00:00 2001 From: Yvan Duhamel Date: Tue, 9 Jul 2019 11:25:48 +0200 Subject: [PATCH 03/11] Simplify method LDAPCPConfig.ApplyConfiguration --- LDAPCP/LDAPCPConfig.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/LDAPCP/LDAPCPConfig.cs b/LDAPCP/LDAPCPConfig.cs index 469e821..48393db 100644 --- a/LDAPCP/LDAPCPConfig.cs +++ b/LDAPCP/LDAPCPConfig.cs @@ -415,14 +415,6 @@ public void ResetCurrentConfiguration() /// public void ApplyConfiguration(LDAPCPConfig configToApply) { - // Copy non-inherited public fields - // This copies persisted field SPTrustName (it doesn't have a corresponding property). - // Private fields should not be retrieved here, since their corresponding properties are retrieved just after. - FieldInfo[] fieldsToCopy = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); - foreach (FieldInfo field in fieldsToCopy) - { - field.SetValue(this, field.GetValue(configToApply)); - } // Copy non-inherited public properties PropertyInfo[] propertiesToCopy = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); foreach (PropertyInfo property in propertiesToCopy) @@ -434,6 +426,9 @@ public void ApplyConfiguration(LDAPCPConfig configToApply) property.SetValue(this, value); } } + + // Member SPTrustName is not exposed through a property, so it must be set explicitly + this.SPTrustName = configToApply.SPTrustName; } /// From e8764940016d6c6f3fd3c701727f177e523fa198 Mon Sep 17 00:00:00 2001 From: Yvan Duhamel Date: Thu, 11 Jul 2019 11:51:16 +0200 Subject: [PATCH 04/11] Update LDAPCPConfig.cs --- LDAPCP/LDAPCPConfig.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LDAPCP/LDAPCPConfig.cs b/LDAPCP/LDAPCPConfig.cs index 48393db..b51991b 100644 --- a/LDAPCP/LDAPCPConfig.cs +++ b/LDAPCP/LDAPCPConfig.cs @@ -496,9 +496,9 @@ public static LDAPCPConfig CreateDefaultConfiguration() /// /// Create a persisted object with default configuration of LDAPCP. If it already exists, it will be deleted. /// - /// GUID of persisted object - /// Name of persisted object - /// Name of the SPTrustedLoginProvider that LDAPCP is associated with + /// GUID of the configuration, stored as a persisted object into SharePoint configuration database + /// Name of the configuration, stored as a persisted object into SharePoint configuration database + /// Name of the SPTrustedLoginProvider that claims provider is associated with /// public static LDAPCPConfig CreateConfiguration(string persistedObjectID, string persistedObjectName, string spTrustName) { From a52aaafa6957e613020c72aa318dbdd0ea137647 Mon Sep 17 00:00:00 2001 From: Yvan Duhamel Date: Thu, 18 Jul 2019 14:08:03 +0200 Subject: [PATCH 05/11] Use helper SPClaimTypes.Equals when possible --- LDAPCP/LDAPCP.cs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/LDAPCP/LDAPCP.cs b/LDAPCP/LDAPCP.cs index 624fe5d..f367419 100644 --- a/LDAPCP/LDAPCP.cs +++ b/LDAPCP/LDAPCP.cs @@ -226,7 +226,8 @@ private bool InitializeClaimTypeConfigList(ClaimTypeConfigCollection nonProcesse { // Search if current claim type in trust exists in ClaimTypeConfigCollection ClaimTypeConfig claimTypeConfig = nonProcessedClaimTypes.FirstOrDefault(x => - String.Equals(x.ClaimType, claimTypeInformation.MappedClaimType, StringComparison.InvariantCultureIgnoreCase) && + !String.IsNullOrWhiteSpace(x.ClaimType) && + SPClaimTypes.Equals(x.ClaimType, claimTypeInformation.MappedClaimType) && !x.UseMainClaimTypeOfDirectoryObject && !String.IsNullOrEmpty(x.LDAPAttribute) && !String.IsNullOrEmpty(x.LDAPClass)); @@ -234,7 +235,7 @@ private bool InitializeClaimTypeConfigList(ClaimTypeConfigCollection nonProcesse if (claimTypeConfig == null) { continue; } claimTypeConfig.ClaimTypeDisplayName = claimTypeInformation.DisplayName; claimTypesSetInTrust.Add(claimTypeConfig); - if (String.Equals(SPTrust.IdentityClaimTypeInformation.MappedClaimType, claimTypeConfig.ClaimType, StringComparison.InvariantCultureIgnoreCase)) + if (SPClaimTypes.Equals(SPTrust.IdentityClaimTypeInformation.MappedClaimType, claimTypeConfig.ClaimType)) { // Identity claim type found, set IdentityClaimTypeConfig property identityClaimTypeFound = true; @@ -245,7 +246,7 @@ private bool InitializeClaimTypeConfigList(ClaimTypeConfigCollection nonProcesse if (!String.IsNullOrEmpty(this.CurrentConfiguration.MainGroupClaimType)) { // If MainGroupClaimType is set, try to set MainGroupClaimTypeConfig with the ClaimTypeConfig that has the same ClaimType - if (String.Equals(claimTypeConfig.ClaimType, this.CurrentConfiguration.MainGroupClaimType, StringComparison.InvariantCultureIgnoreCase)) + if (SPClaimTypes.Equals(claimTypeConfig.ClaimType, this.CurrentConfiguration.MainGroupClaimType)) { MainGroupClaimTypeConfig = claimTypeConfig; groupClaimTypeFound = true; @@ -501,7 +502,7 @@ protected override void FillSearch(Uri context, string[] entityTypes, string sea { ClaimTypeConfig ctConfig = ProcessedClaimTypesList.FirstOrDefault(x => !x.UseMainClaimTypeOfDirectoryObject && - String.Equals(x.ClaimType, entity.Claim.ClaimType, StringComparison.InvariantCultureIgnoreCase)); + SPClaimTypes.Equals(x.ClaimType, entity.Claim.ClaimType)); string nodeName = ctConfig != null ? ctConfig.ClaimTypeDisplayName : entity.Claim.ClaimType; matchNode = new SPProviderHierarchyNode(_ProviderInternalName, nodeName, entity.Claim.ClaimType, true); @@ -1156,7 +1157,7 @@ protected virtual void AugmentEntity(Uri context, SPClaim entity, SPClaimProvide } IEnumerable allGroupsCTConfig = this.ProcessedClaimTypesList.Where(x => x.EntityType == DirectoryObjectType.Group && !x.UseMainClaimTypeOfDirectoryObject); - ClaimTypeConfig mainGroupCTConfig = allGroupsCTConfig.FirstOrDefault(x => String.Equals(x.ClaimType, this.CurrentConfiguration.MainGroupClaimType, StringComparison.InvariantCultureIgnoreCase)); + ClaimTypeConfig mainGroupCTConfig = allGroupsCTConfig.FirstOrDefault(x => SPClaimTypes.Equals(x.ClaimType, this.CurrentConfiguration.MainGroupClaimType)); if (mainGroupCTConfig == null) { ClaimsProviderLogging.Log($"[{ProviderInternalName}] Configuration for claim type '{this.CurrentConfiguration.MainGroupClaimType}' cannot be found, please add it in claim types configuration list.", @@ -1184,7 +1185,7 @@ protected virtual void AugmentEntity(Uri context, SPClaim entity, SPClaimProvide if (ldapConnection.GetGroupMembershipUsingDotNetHelpers) { directoryGroups = GetGroupsFromActiveDirectory(ldapConnection, currentContext, mainGroupCTConfig); - directoryGroups.AddRange(GetGroupsFromLDAPDirectory(ldapConnection, currentContext, allGroupsCTConfig.Where(x => !String.Equals(x.ClaimType, this.CurrentConfiguration.MainGroupClaimType, StringComparison.InvariantCultureIgnoreCase)))); + directoryGroups.AddRange(GetGroupsFromLDAPDirectory(ldapConnection, currentContext, allGroupsCTConfig.Where(x => !SPClaimTypes.Equals(x.ClaimType, this.CurrentConfiguration.MainGroupClaimType)))); } else { @@ -1273,7 +1274,7 @@ protected virtual List GetGroupsFromActiveDirectory(LDAPConnection ldap } // https://github.com/Yvand/LDAPCP/issues/22: UserPrincipal.FindByIdentity() doesn't support emails, so if IncomingEntity is an email, user needs to be retrieved in a different way - if (String.Equals(currentContext.IncomingEntity.ClaimType, WIF4_5.ClaimTypes.Email, StringComparison.InvariantCultureIgnoreCase)) + if (SPClaimTypes.Equals(currentContext.IncomingEntity.ClaimType, WIF4_5.ClaimTypes.Email)) { using (UserPrincipal userEmailPrincipal = new UserPrincipal(principalContext) { Enabled = true, EmailAddress = currentContext.IncomingEntity.Value }) { @@ -1596,7 +1597,7 @@ protected virtual SPClaim CreateClaim(string type, string value, string valueTyp { string claimValue = String.Empty; //var attr = ProcessedAttributes.Where(x => x.ClaimTypeProp == type).FirstOrDefault(); - var attr = ProcessedClaimTypesList.FirstOrDefault(x => String.Equals(x.ClaimType, type, StringComparison.InvariantCultureIgnoreCase)); + var attr = ProcessedClaimTypesList.FirstOrDefault(x => SPClaimTypes.Equals(x.ClaimType, type)); //if (inputHasKeyword && attr.DoNotAddPrefixIfInputHasKeywordProp) if ((!inputHasKeyword || !attr.DoNotAddClaimValuePrefixIfBypassLookup) && !HasPrefixToken(attr.ClaimValuePrefix, ClaimsProviderConstants.LDAPCPCONFIG_TOKENDOMAINNAME) && @@ -1620,7 +1621,7 @@ protected virtual PickerEntity CreatePickerEntityHelper(ConsolidatedResult resul string permissionClaimType = result.ClaimTypeConfig.ClaimType; bool isIdentityClaimType = false; - if ((String.Equals(permissionClaimType, SPTrust.IdentityClaimTypeInformation.MappedClaimType, StringComparison.InvariantCultureIgnoreCase) + if ((SPClaimTypes.Equals(permissionClaimType, SPTrust.IdentityClaimTypeInformation.MappedClaimType) || result.ClaimTypeConfig.UseMainClaimTypeOfDirectoryObject) && result.ClaimTypeConfig.LDAPClass == IdentityClaimTypeConfig.LDAPClass) { isIdentityClaimType = true; @@ -1705,7 +1706,7 @@ protected virtual string FormatPermissionValue(string claimType, string claimVal { string value = claimValue; - var attr = ProcessedClaimTypesList.FirstOrDefault(x => String.Equals(x.ClaimType, claimType, StringComparison.InvariantCultureIgnoreCase)); + var attr = ProcessedClaimTypesList.FirstOrDefault(x => SPClaimTypes.Equals(x.ClaimType, claimType)); if (HasPrefixToken(attr.ClaimValuePrefix, ClaimsProviderConstants.LDAPCPCONFIG_TOKENDOMAINNAME)) { value = string.Format("{0}{1}", attr.ClaimValuePrefix.Replace(ClaimsProviderConstants.LDAPCPCONFIG_TOKENDOMAINNAME, domainName), value); @@ -1861,7 +1862,7 @@ protected virtual List CreatePickerEntityForSpecificClaimTypes(str ConsolidatedResult result = new ConsolidatedResult(); result.ClaimTypeConfig = ctConfig; result.Value = input; - bool isIdentityClaimType = String.Equals(claim.ClaimType, IdentityClaimTypeConfig.ClaimType, StringComparison.InvariantCultureIgnoreCase); + bool isIdentityClaimType = SPClaimTypes.Equals(claim.ClaimType, IdentityClaimTypeConfig.ClaimType); pe.DisplayText = FormatPermissionDisplayText(pe, isIdentityClaimType, result); entities.Add(pe); From 653e1d9f17f301474b41df089fd23b2569d283cb Mon Sep 17 00:00:00 2001 From: Yvan Duhamel Date: Thu, 18 Jul 2019 18:00:18 +0200 Subject: [PATCH 06/11] Investigating issue https://github.com/Yvand/LDAPCP/issues/87 --- LDAPCP/LDAPCP.cs | 28 +++++++------------- LDAPCP/LDAPCPConfig.cs | 60 +++++++++++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/LDAPCP/LDAPCP.cs b/LDAPCP/LDAPCP.cs index f367419..cc7b904 100644 --- a/LDAPCP/LDAPCP.cs +++ b/LDAPCP/LDAPCP.cs @@ -1006,28 +1006,20 @@ protected virtual void SetLDAPConnection(Uri currentContext, LDAPConnection ldap ldapConnection.AuthenticationSettings = ldapConnection.Directory.AuthenticationType; } - // Operations in this block do LDAP queries, so let's monitor their execution time + // This block does LDAP operations using (new SPMonitoredScope($"[{ProviderInternalName}] Get domain names / root container information about LDAP server \"{ldapConnection.Directory.Path}\"", 2000)) { // Retrieve FQDN and domain name of current DirectoryEntry string domainName = String.Empty; string domainFQDN = String.Empty; - try - { - // Operations below may fail if LDAP server cannot be reached - // Cache those values for the whole lifetime of the process, because getting them triggers LDAP queries in the background - OperationContext.GetDomainInformation(ldapConnection.Directory, out domainName, out domainFQDN); - ldapConnection.DomainName = domainName; - ldapConnection.DomainFQDN = domainFQDN; - if (ldapConnection.Directory.Properties.Contains("distinguishedName")) - { - ldapConnection.RootContainer = ldapConnection.Directory.Properties["distinguishedName"].Value.ToString(); - } - } - catch (Exception ex) - { - ClaimsProviderLogging.LogException(ProviderInternalName, $"while getting domain names information for LDAP connection {ldapConnection.Directory.Path}", TraceCategory.Configuration, ex); - } + string domaindistinguishedName = String.Empty; + + // If there is no existing LDAPCP configuration, this method will be called each time (LDAPConnection properties will be null) + OperationContext.GetDomainInformation(ldapConnection.Directory, out domaindistinguishedName, out domainName, out domainFQDN); + // Cache those values for the whole lifetime of the process, because getting them requires LDAP operations + ldapConnection.RootContainer = domaindistinguishedName; + ldapConnection.DomainName = domainName; + ldapConnection.DomainFQDN = domainFQDN; } } @@ -1256,7 +1248,7 @@ protected virtual List GetGroupsFromActiveDirectory(LDAPConnection ldap Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); UserPrincipal adUser = null; - PrincipalContext principalContext = null; + PrincipalContext principalContext = null; try { using (new SPMonitoredScope($"[{ProviderInternalName}] Get AD Principal of user {currentContext.IncomingEntity.Value} " + directoryDetails, 1000)) diff --git a/LDAPCP/LDAPCPConfig.cs b/LDAPCP/LDAPCPConfig.cs index b51991b..f666b38 100644 --- a/LDAPCP/LDAPCPConfig.cs +++ b/LDAPCP/LDAPCPConfig.cs @@ -423,7 +423,9 @@ public void ApplyConfiguration(LDAPCPConfig configToApply) { object value = property.GetValue(configToApply); if (value != null) + { property.SetValue(this, value); + } } } @@ -1254,24 +1256,52 @@ public static void GetDomainInformation(string distinguishedName, out string dom /// LDAP Server to query /// Domain name /// Fully qualified domain name - public static void GetDomainInformation(DirectoryEntry directory, out string domainName, out string domainFQDN) + public static void GetDomainInformation(DirectoryEntry directory, out string domaindistinguishedName, out string domainName, out string domainFQDN) { - string distinguishedName = String.Empty; - domainName = domainFQDN = String.Empty; - // Method PropertyCollection.Contains("distinguishedName") does a LDAP bind behind the scene - if (directory.Properties.Contains("distinguishedName")) - { - distinguishedName = directory.Properties["distinguishedName"].Value.ToString(); - GetDomainInformation(distinguishedName, out domainName, out domainFQDN); - } - else + bool success = false; + domaindistinguishedName = String.Empty; + domainName = String.Empty; + domainFQDN = String.Empty; + int count = 0; + do { - // This logic to get the domainName may not work with AD LDS: - // if distinguishedName = "CN=Partition1,DC=MyLDS,DC=local", then both "name" and "cn" = "Partition1", while we expect "MyLDS" - // So now it's only made if the distinguishedName is not available (very unlikely codepath) - if (directory.Properties.Contains("name")) domainName = directory.Properties["name"].Value.ToString(); - else if (directory.Properties.Contains("cn")) domainName = directory.Properties["cn"].Value.ToString(); // Tivoli sets domain name in cn property (property name does not exist) + count++; + try + { +#if DEBUG + directory.AuthenticationType = AuthenticationTypes.None; + ClaimsProviderLogging.Log($"directory.AuthenticationType = {directory.AuthenticationType}", TraceSeverity.Unexpected, EventSeverity.Error, TraceCategory.Configuration); +#endif + + // Method PropertyCollection.Contains("distinguishedName") does a LDAP bind + // In AD LDS: property "distinguishedName" = "CN=LDSInstance2,DC=ADLDS,DC=local", properties "name" and "cn" = "LDSInstance2" + if (directory.Properties.Contains("distinguishedName")) + { + domaindistinguishedName = directory.Properties["distinguishedName"].Value.ToString(); + GetDomainInformation(domaindistinguishedName, out domainName, out domainFQDN); + } + else if (directory.Properties.Contains("name")) + { + domainName = directory.Properties["name"].Value.ToString(); + } + else if (directory.Properties.Contains("cn")) + { + // Tivoli stores domain name in property "cn" (properties "distinguishedName" and "name" don't exist) + domainName = directory.Properties["cn"].Value.ToString(); + } + + success = true; + } + catch (DirectoryServicesCOMException ex) + { + ClaimsProviderLogging.LogException("", $"while getting domain names information for LDAP connection {directory.Path} (DirectoryServicesCOMException) at attempt {count}", TraceCategory.Configuration, ex); + } + catch (Exception ex) + { + ClaimsProviderLogging.LogException("", $"while getting domain names information for LDAP connection {directory.Path} (Exception) at attempt {count}", TraceCategory.Configuration, ex); + } } + while (success == false && count < 5); } /// From 8b024e268a4b499c97916e39674dde1430b0ffdb Mon Sep 17 00:00:00 2001 From: Yvan Duhamel Date: Tue, 6 Aug 2019 13:21:31 +0200 Subject: [PATCH 07/11] Test workaround to bug https://github.com/Yvand/LDAPCP/issues/87 --- LDAPCP/LDAPCP.cs | 47 +++++++++++++-------- LDAPCP/LDAPCPConfig.cs | 74 ++++++++++++++++++++-------------- LDAPCP/LdapcpLoggingService.cs | 2 +- 3 files changed, 75 insertions(+), 48 deletions(-) diff --git a/LDAPCP/LDAPCP.cs b/LDAPCP/LDAPCP.cs index cc7b904..3c6a2e1 100644 --- a/LDAPCP/LDAPCP.cs +++ b/LDAPCP/LDAPCP.cs @@ -909,8 +909,8 @@ protected bool QueryLDAPServers(OperationContext currentContext, List !String.IsNullOrEmpty(x.Filter)), ldapConnection => { - Debug.WriteLine($"ldapConnection: Path: {ldapConnection.LDAPPath}, UseSPServerConnectionToAD: {ldapConnection.UseSPServerConnectionToAD}"); - ClaimsProviderLogging.LogDebug($"ldapConnection: Path: {ldapConnection.LDAPPath}, UseSPServerConnectionToAD: {ldapConnection.UseSPServerConnectionToAD}"); + Debug.WriteLine($"ldapConnection: Path: {ldapConnection.Directory.Path}, UseSPServerConnectionToAD: {ldapConnection.UseSPServerConnectionToAD}"); + ClaimsProviderLogging.LogDebug($"ldapConnection: Path: {ldapConnection.Directory.Path}, UseSPServerConnectionToAD: {ldapConnection.UseSPServerConnectionToAD}"); #pragma warning disable CS0618 // Type or member is obsolete SetLDAPConnection(currentContext, ldapConnection); #pragma warning restore CS0618 // Type or member is obsolete @@ -1001,25 +1001,40 @@ protected virtual void SetLDAPConnection(Uri currentContext, LDAPConnection ldap } else { - ldapConnection.Directory = Domain.GetComputerDomain().GetDirectoryEntry(); + Domain computerDomain = Domain.GetComputerDomain(); + ldapConnection.Directory = computerDomain.GetDirectoryEntry(); + ldapConnection.DomainFQDN = computerDomain.Name; + ldapConnection.DomainName = OperationContext.GetDomainName(ldapConnection.DomainFQDN); // Property LDAPConnection.AuthenticationSettings must be set, in order to build the PrincipalContext correctly in GetGroupsFromActiveDirectory() ldapConnection.AuthenticationSettings = ldapConnection.Directory.AuthenticationType; } - // This block does LDAP operations - using (new SPMonitoredScope($"[{ProviderInternalName}] Get domain names / root container information about LDAP server \"{ldapConnection.Directory.Path}\"", 2000)) + if (String.IsNullOrEmpty(ldapConnection.RootContainer) || String.IsNullOrEmpty(ldapConnection.DomainFQDN) || String.IsNullOrEmpty(ldapConnection.DomainName)) { - // Retrieve FQDN and domain name of current DirectoryEntry - string domainName = String.Empty; - string domainFQDN = String.Empty; - string domaindistinguishedName = String.Empty; - - // If there is no existing LDAPCP configuration, this method will be called each time (LDAPConnection properties will be null) - OperationContext.GetDomainInformation(ldapConnection.Directory, out domaindistinguishedName, out domainName, out domainFQDN); - // Cache those values for the whole lifetime of the process, because getting them requires LDAP operations - ldapConnection.RootContainer = domaindistinguishedName; - ldapConnection.DomainName = domainName; - ldapConnection.DomainFQDN = domainFQDN; + // This block does LDAP operations + using (new SPMonitoredScope($"[{ProviderInternalName}] Get domain names / root container information about LDAP server \"{ldapConnection.Directory.Path}\"", 2000)) + { + // Retrieve FQDN and domain name of current DirectoryEntry + string domainName = String.Empty; + string domainFQDN = String.Empty; + string domaindistinguishedName = String.Empty; + + // If there is no existing LDAPCP configuration, this method will be called each time as property LDAPConnection.RootContainer will be null + OperationContext.GetDomainInformation(ldapConnection.Directory, out domaindistinguishedName, out domainName, out domainFQDN); + // Cache those values for the whole lifetime of the process, because getting them requires LDAP operations + if (!String.IsNullOrWhiteSpace(domaindistinguishedName)) + { + ldapConnection.RootContainer = domaindistinguishedName; + } + if (!String.IsNullOrWhiteSpace(domainName)) + { + ldapConnection.DomainName = domainName; + } + if (!String.IsNullOrWhiteSpace(domainFQDN)) + { + ldapConnection.DomainFQDN = domainFQDN; + } + } } } diff --git a/LDAPCP/LDAPCPConfig.cs b/LDAPCP/LDAPCPConfig.cs index f666b38..d9ed608 100644 --- a/LDAPCP/LDAPCPConfig.cs +++ b/LDAPCP/LDAPCPConfig.cs @@ -1222,6 +1222,21 @@ public static string GetFirstSubString(string value, string separator) return (stop > -1) ? value.Substring(0, stop) : string.Empty; } + /// + /// Return the domain name from the domain FQDN + /// + /// Fully qualified domain name + /// Domain name + public static string GetDomainName(string domainFQDN) + { + string domainName = String.Empty; + if (domainFQDN.Contains(".")) + { + domainName = domainFQDN.Split(new char[] { '.' })[0]; + } + return domainName; + } + /// /// Extract domain name information from the distinguishedName supplied /// @@ -1256,52 +1271,49 @@ public static void GetDomainInformation(string distinguishedName, out string dom /// LDAP Server to query /// Domain name /// Fully qualified domain name - public static void GetDomainInformation(DirectoryEntry directory, out string domaindistinguishedName, out string domainName, out string domainFQDN) + public static bool GetDomainInformation(DirectoryEntry directory, out string domaindistinguishedName, out string domainName, out string domainFQDN) { bool success = false; domaindistinguishedName = String.Empty; domainName = String.Empty; domainFQDN = String.Empty; - int count = 0; - do + + try { - count++; - try - { #if DEBUG - directory.AuthenticationType = AuthenticationTypes.None; - ClaimsProviderLogging.Log($"directory.AuthenticationType = {directory.AuthenticationType}", TraceSeverity.Unexpected, EventSeverity.Error, TraceCategory.Configuration); + directory.AuthenticationType = AuthenticationTypes.None; + ClaimsProviderLogging.LogDebug($"Hardcoded property DirectoryEntry.AuthenticationType to {directory.AuthenticationType} for \"{directory.Path}\""); #endif - // Method PropertyCollection.Contains("distinguishedName") does a LDAP bind - // In AD LDS: property "distinguishedName" = "CN=LDSInstance2,DC=ADLDS,DC=local", properties "name" and "cn" = "LDSInstance2" - if (directory.Properties.Contains("distinguishedName")) - { - domaindistinguishedName = directory.Properties["distinguishedName"].Value.ToString(); - GetDomainInformation(domaindistinguishedName, out domainName, out domainFQDN); - } - else if (directory.Properties.Contains("name")) - { - domainName = directory.Properties["name"].Value.ToString(); - } - else if (directory.Properties.Contains("cn")) - { - // Tivoli stores domain name in property "cn" (properties "distinguishedName" and "name" don't exist) - domainName = directory.Properties["cn"].Value.ToString(); - } - - success = true; + // Method PropertyCollection.Contains("distinguishedName") does a LDAP bind + // In AD LDS: property "distinguishedName" = "CN=LDSInstance2,DC=ADLDS,DC=local", properties "name" and "cn" = "LDSInstance2" + if (directory.Properties.Contains("distinguishedName")) + { + domaindistinguishedName = directory.Properties["distinguishedName"].Value.ToString(); + GetDomainInformation(domaindistinguishedName, out domainName, out domainFQDN); } - catch (DirectoryServicesCOMException ex) + else if (directory.Properties.Contains("name")) { - ClaimsProviderLogging.LogException("", $"while getting domain names information for LDAP connection {directory.Path} (DirectoryServicesCOMException) at attempt {count}", TraceCategory.Configuration, ex); + domainName = directory.Properties["name"].Value.ToString(); } - catch (Exception ex) + else if (directory.Properties.Contains("cn")) { - ClaimsProviderLogging.LogException("", $"while getting domain names information for LDAP connection {directory.Path} (Exception) at attempt {count}", TraceCategory.Configuration, ex); + // Tivoli stores domain name in property "cn" (properties "distinguishedName" and "name" don't exist) + domainName = directory.Properties["cn"].Value.ToString(); } + + success = true; } - while (success == false && count < 5); + catch (DirectoryServicesCOMException ex) + { + ClaimsProviderLogging.LogException("", $"while getting domain names information for LDAP connection {directory.Path} (DirectoryServicesCOMException)", TraceCategory.Configuration, ex); + } + catch (Exception ex) + { + ClaimsProviderLogging.LogException("", $"while getting domain names information for LDAP connection {directory.Path} (Exception)", TraceCategory.Configuration, ex); + } + + return success; } /// diff --git a/LDAPCP/LdapcpLoggingService.cs b/LDAPCP/LdapcpLoggingService.cs index 4151d13..877cd42 100644 --- a/LDAPCP/LdapcpLoggingService.cs +++ b/LDAPCP/LdapcpLoggingService.cs @@ -118,7 +118,7 @@ public static void LogDebug(string message) try { #if DEBUG - WriteTrace(TraceCategory.Debug, TraceSeverity.VerboseEx, message); + WriteTrace(TraceCategory.Debug, TraceSeverity.Verbose, message); Debug.WriteLine(message); #else // Do nothing From c7daef4d197b844add2cdc0a75e1c916ad724b4f Mon Sep 17 00:00:00 2001 From: Yvan Duhamel Date: Tue, 20 Aug 2019 11:30:02 +0200 Subject: [PATCH 08/11] Fixing https://github.com/Yvand/LDAPCP/issues/87 --- LDAPCP/LDAPCP.cs | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/LDAPCP/LDAPCP.cs b/LDAPCP/LDAPCP.cs index 3c6a2e1..4687fad 100644 --- a/LDAPCP/LDAPCP.cs +++ b/LDAPCP/LDAPCP.cs @@ -1003,8 +1003,11 @@ protected virtual void SetLDAPConnection(Uri currentContext, LDAPConnection ldap { Domain computerDomain = Domain.GetComputerDomain(); ldapConnection.Directory = computerDomain.GetDirectoryEntry(); + + // Set properties LDAPConnection.DomainFQDN and LDAPConnection.DomainName here as a workaround to issue https://github.com/Yvand/LDAPCP/issues/87 ldapConnection.DomainFQDN = computerDomain.Name; ldapConnection.DomainName = OperationContext.GetDomainName(ldapConnection.DomainFQDN); + // Property LDAPConnection.AuthenticationSettings must be set, in order to build the PrincipalContext correctly in GetGroupsFromActiveDirectory() ldapConnection.AuthenticationSettings = ldapConnection.Directory.AuthenticationType; } @@ -1893,17 +1896,20 @@ protected override void FillSchema(Microsoft.SharePoint.WebControls.SPProviderSc /// /// Return the identity claim type /// - /// + /// Identity claim type. Should not return null to prevent exceptions in SharePoint when users sign-in public override string GetClaimTypeForUserKey() { - // Initialization may fail because there is no yet configuration (fresh install) - // In this case, LDAPCP should not return null because it causes null exceptions in SharePoint when users sign-in + // Elevation of privileges when calling LDAPCP.Initialize is very important to prevent issue https://github.com/Yvand/LDAPCP/issues/87 + // But calling SPSecurity.RunWithElevatedPrivileges here is not possible as it causes a StackOverflowException Initialize(null, null); this.Lock_Config.EnterReadLock(); try { - if (SPTrust == null) { return String.Empty; } + if (SPTrust == null) + { + return String.Empty; + } return SPTrust.IdentityClaimTypeInformation.MappedClaimType; } @@ -1921,20 +1927,27 @@ public override string GetClaimTypeForUserKey() /// /// Return the user key (SPClaim with identity claim type) from the incoming entity /// - /// + /// SPClaim corresponding to the user key of the incoming entity. Should not return null to prevent exceptions in SharePoint when users sign-in /// protected override SPClaim GetUserKeyForEntity(SPClaim entity) { - // Initialization may fail because there is no yet configuration (fresh install) - // In this case, LDAPCP should not return null because it causes null exceptions in SharePoint when users sign-in - bool initSucceeded = Initialize(null, null); + bool initSucceeded = false; + + // Elevation of privileges when calling LDAPCP.Initialize is very important to prevent issue https://github.com/Yvand/LDAPCP/issues/87 + SPSecurity.RunWithElevatedPrivileges(delegate () + { + initSucceeded = Initialize(null, null); + }); this.Lock_Config.EnterReadLock(); try { // If initialization failed but SPTrust is not null, rest of the method can be executed normally // Otherwise return the entity - if (!initSucceeded && SPTrust == null) { return entity; } + if (!initSucceeded && SPTrust == null) + { + return entity; + } // There are 2 scenarios: // 1: OriginalIssuer is "SecurityTokenService": Value looks like "05.t|yvanhost|yvand@yvanhost.local", claim type is "http://schemas.microsoft.com/sharepoint/2009/08/claims/userid" and it must be decoded properly From a5c164680f44fbf778446bc91b0f59f887f557a5 Mon Sep 17 00:00:00 2001 From: Yvan Duhamel Date: Tue, 20 Aug 2019 15:03:45 +0200 Subject: [PATCH 09/11] Fix https://github.com/Yvand/LDAPCP/issues/87 --- LDAPCP/LDAPCP.cs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/LDAPCP/LDAPCP.cs b/LDAPCP/LDAPCP.cs index 4687fad..5a4ed88 100644 --- a/LDAPCP/LDAPCP.cs +++ b/LDAPCP/LDAPCP.cs @@ -109,11 +109,27 @@ protected bool Initialize(Uri context, string[] entityTypes) globalConfiguration = GetConfiguration(context, entityTypes, PersistedObjectName, SPTrust.Name); if (globalConfiguration == null) { - ClaimsProviderLogging.Log($"[{ProviderInternalName}] Configuration '{PersistedObjectName}' was not foundin configuration database, use default configuration instead. Visit LDAPCP admin pages in central administration to create it.", - TraceSeverity.Unexpected, EventSeverity.Error, TraceCategory.Core); - // Run with default configuration, which creates a connection to connect to current AD domain globalConfiguration = LDAPCPConfig.ReturnDefaultConfiguration(SPTrust.Name); - refreshConfig = true; + // There is no thread safety issue in reading this.CurrentConfiguration here, thanks to the lock Sync_Init + if (this.CurrentConfiguration == null) + { + ClaimsProviderLogging.Log($"[{ProviderInternalName}] Configuration '{PersistedObjectName}' was not found in configuration database, switch to default. Visit LDAPCP admin pages in central administration to create it.", + TraceSeverity.Unexpected, EventSeverity.Error, TraceCategory.Core); + // Run with default configuration, which creates a connection to connect to current AD domain + refreshConfig = true; + } + else + { + // Default configuration has only 1 connection to AD domain of current SP server. + // If its property LDAPConnection.RootContainer is null, it means that LDAPConnection properties were not set because it was previously done in a thread that did not run as application pool account (https://github.com/Yvand/LDAPCP/issues/87) + // If so, recreate defalt configuration + if (String.IsNullOrEmpty(this.CurrentConfiguration.LDAPConnectionsProp.First().RootContainer)) + { + ClaimsProviderLogging.Log($"[{ProviderInternalName}] Default configuration was not fully initialized, recreating it...", + TraceSeverity.Unexpected, EventSeverity.Error, TraceCategory.Core); + refreshConfig = true; + } + } } else { @@ -1932,7 +1948,7 @@ public override string GetClaimTypeForUserKey() protected override SPClaim GetUserKeyForEntity(SPClaim entity) { bool initSucceeded = false; - + // Elevation of privileges when calling LDAPCP.Initialize is very important to prevent issue https://github.com/Yvand/LDAPCP/issues/87 SPSecurity.RunWithElevatedPrivileges(delegate () { From dee7f5e2684f80fce9db48c0d3cd8a5668f246d9 Mon Sep 17 00:00:00 2001 From: Yvan Duhamel Date: Tue, 20 Aug 2019 16:20:12 +0200 Subject: [PATCH 10/11] Fix for bug https://github.com/Yvand/LDAPCP/issues/87 in scenario where local config already exists but must be recreated --- LDAPCP/LDAPCP.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/LDAPCP/LDAPCP.cs b/LDAPCP/LDAPCP.cs index 5a4ed88..4f556a9 100644 --- a/LDAPCP/LDAPCP.cs +++ b/LDAPCP/LDAPCP.cs @@ -122,7 +122,7 @@ protected bool Initialize(Uri context, string[] entityTypes) { // Default configuration has only 1 connection to AD domain of current SP server. // If its property LDAPConnection.RootContainer is null, it means that LDAPConnection properties were not set because it was previously done in a thread that did not run as application pool account (https://github.com/Yvand/LDAPCP/issues/87) - // If so, recreate defalt configuration + // If so, recreate default configuration if (String.IsNullOrEmpty(this.CurrentConfiguration.LDAPConnectionsProp.First().RootContainer)) { ClaimsProviderLogging.Log($"[{ProviderInternalName}] Default configuration was not fully initialized, recreating it...", @@ -156,14 +156,24 @@ protected bool Initialize(Uri context, string[] entityTypes) { if (this.CurrentConfigurationVersion == ((SPPersistedObject)globalConfiguration).Version) { - ClaimsProviderLogging.Log($"[{ProviderInternalName}] Configuration '{PersistedObjectName}' was found, version {((SPPersistedObject)globalConfiguration).Version.ToString()}", + ClaimsProviderLogging.Log($"[{ProviderInternalName}] Configuration '{PersistedObjectName}' version {((SPPersistedObject)globalConfiguration).Version.ToString()} was found", TraceSeverity.VerboseEx, EventSeverity.Information, TraceCategory.Core); + + // Local configuration may need to be recreated due to bug https://github.com/Yvand/LDAPCP/issues/87: + // If a LDAPConnection with UseSPServerConnectionToAD true has its property LDAPConnection.RootContainer null, it means that LDAPConnection properties failed to be set, because thread did not run as application pool account + if (this.CurrentConfiguration != null && + this.CurrentConfiguration.LDAPConnectionsProp.FirstOrDefault(x => x.UseSPServerConnectionToAD && String.IsNullOrEmpty(x.RootContainer)) != null) + { + ClaimsProviderLogging.Log($"[{ProviderInternalName}] Configuration '{PersistedObjectName}' version {((SPPersistedObject)globalConfiguration).Version.ToString()} was found, but local copy is not fully initialized. Refreshing local copy...", + TraceSeverity.Medium, EventSeverity.Information, TraceCategory.Core); + refreshConfig = true; + } } else { refreshConfig = true; this.CurrentConfigurationVersion = ((SPPersistedObject)globalConfiguration).Version; - ClaimsProviderLogging.Log($"[{ProviderInternalName}] Configuration '{PersistedObjectName}' changed to version {((SPPersistedObject)globalConfiguration).Version.ToString()}, refreshing local copy", + ClaimsProviderLogging.Log($"[{ProviderInternalName}] Configuration '{PersistedObjectName}' changed to version {((SPPersistedObject)globalConfiguration).Version.ToString()}, refreshing local copy...", TraceSeverity.Medium, EventSeverity.Information, TraceCategory.Core); } } From 112ac3e546cc078caf3077af219751deedead84f Mon Sep 17 00:00:00 2001 From: Yvan Duhamel Date: Wed, 21 Aug 2019 09:44:06 +0200 Subject: [PATCH 11/11] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d155c1..dfb822e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Add method LDAPCPConfig.CreateDefaultConfiguration +* Fix bug: randomly, LDAPCP returned results that were missing their domain name. In SharePoint logs, a DirectoryServicesCOMException error was recorded. https://github.com/Yvand/LDAPCP/issues/87 ## LDAPCP 13.0.20190621.905 enhancements & bug-fixes - Published in June 21, 2019