diff --git a/Yvand.LDAPCPSE/Yvand.LDAPCPSE.csproj b/Yvand.LDAPCPSE/Yvand.LDAPCPSE.csproj index c1a3aaa..ff01a1e 100644 --- a/Yvand.LDAPCPSE/Yvand.LDAPCPSE.csproj +++ b/Yvand.LDAPCPSE/Yvand.LDAPCPSE.csproj @@ -65,6 +65,7 @@ False references\SPSE\Microsoft.SharePoint.dll + False diff --git a/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/Configuration/ClaimsProviderConstants.cs b/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/Configuration/ClaimsProviderConstants.cs index 98cf5a6..d585dce 100644 --- a/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/Configuration/ClaimsProviderConstants.cs +++ b/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/Configuration/ClaimsProviderConstants.cs @@ -312,7 +312,7 @@ public class OperationContext /// public List CurrentClaimTypeConfigList { get; private set; } - public List LdapConnections { get; private set; } + public List LdapConnections { get; set; } public OperationContext(ClaimsProviderSettings settings, OperationType currentRequestType, string input, SPClaim incomingEntity, Uri context, string[] entityTypes, string hierarchyNodeID, int maxCount) { diff --git a/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/Configuration/DirectoryConnection.cs b/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/Configuration/DirectoryConnection.cs index 695c9ec..367397f 100644 --- a/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/Configuration/DirectoryConnection.cs +++ b/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/Configuration/DirectoryConnection.cs @@ -105,6 +105,17 @@ public string[] GroupMembershipLdapAttributes [Persisted] private string[] _GroupMembershipLdapAttributes = new string[] { "memberOf", "uniquememberof" }; + /// + /// Get or set a LDAP filter specific to this LDAP connection + /// + public string CustomFilter + { + get { return _CustomFilter; } + set { _CustomFilter = value; } + } + [Persisted] + private string _CustomFilter; + /// /// DirectoryEntry used to make LDAP queries /// diff --git a/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/LDAPCPSE.cs b/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/LDAPCPSE.cs index 47650c7..45caa31 100644 --- a/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/LDAPCPSE.cs +++ b/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/LDAPCPSE.cs @@ -385,6 +385,7 @@ protected void AugmentEntity(Uri context, SPClaim entity, SPClaimProviderContext Logger.Log($"[{Name}] Starting augmentation for user '{decodedEntity.Value}'.", TraceSeverity.Verbose, EventSeverity.Information, TraceCategory.Augmentation); currentContext = new OperationContext(this.Settings as ClaimsProviderSettings, OperationType.Augmentation, String.Empty, decodedEntity, context, null, null, Int32.MaxValue); + ValidateRuntimeSettings(currentContext); Stopwatch timer = new Stopwatch(); timer.Start(); List groups = this.EntityProvider.GetEntityGroups(currentContext); @@ -439,6 +440,7 @@ protected override void FillResolve(Uri context, string[] entityTypes, string re try { OperationContext currentContext = new OperationContext(this.Settings as ClaimsProviderSettings, OperationType.Search, resolveInput, null, context, entityTypes, null, 30); + ValidateRuntimeSettings(currentContext); List entities = SearchOrValidate(currentContext); if (entities == null || entities.Count == 0) { return; } foreach (PickerEntity entity in entities) @@ -470,6 +472,7 @@ protected override void FillSearch(Uri context, string[] entityTypes, string sea try { OperationContext currentContext = new OperationContext(this.Settings as ClaimsProviderSettings, OperationType.Search, searchPattern, null, context, entityTypes, hierarchyNodeID, maxCount); + ValidateRuntimeSettings(currentContext); List entities = this.SearchOrValidate(currentContext); if (entities == null || entities.Count == 0) { return; } SPProviderHierarchyNode matchNode = null; @@ -523,6 +526,7 @@ protected override void FillResolve(Uri context, string[] entityTypes, SPClaim r if (!String.Equals(resolveInput.OriginalIssuer, this.OriginalIssuerName, StringComparison.InvariantCultureIgnoreCase)) { return; } OperationContext currentContext = new OperationContext(this.Settings as ClaimsProviderSettings, OperationType.Validation, resolveInput.Value, resolveInput, context, entityTypes, null, 1); + ValidateRuntimeSettings(currentContext); List entities = this.SearchOrValidate(currentContext); if (entities?.Count == 1) { @@ -947,6 +951,10 @@ protected virtual string FormatPermissionDisplayText(LdapEntityProviderResult di } return entityDisplayText; } + + public virtual void ValidateRuntimeSettings(OperationContext operationContext) + { + } #endregion /// diff --git a/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/LdapEntityProvider.cs b/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/LdapEntityProvider.cs index 2cda0bf..2038c5d 100644 --- a/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/LdapEntityProvider.cs +++ b/Yvand.LDAPCPSE/Yvand.LdapClaimsProvider/LdapEntityProvider.cs @@ -411,30 +411,42 @@ public override List SearchOrValidateEntities(Operatio return new List(0); } - string ldapFilter = this.BuildFilter(currentContext); + //string ldapFilter = this.BuildFilter(currentContext); List LdapSearchResult = null; SPSecurity.RunWithElevatedPrivileges(delegate () { - LdapSearchResult = this.QueryLDAPServers(currentContext, ldapFilter); + LdapSearchResult = this.QueryLDAPServers(currentContext); }); return LdapSearchResult; } - protected string BuildFilter(OperationContext currentContext) + protected string BuildFilter(List claimTypeConfigList, string inputText, bool exactSearch, DirectoryConnection ldapConnection) { + if (ldapConnection != null && String.IsNullOrWhiteSpace(ldapConnection.CustomFilter)) + { // In this case, the generic LDAP filter can be used + return String.Empty; + } + StringBuilder filter = new StringBuilder(); if (this.Settings.FilterEnabledUsersOnly) { filter.Append(ClaimsProviderConstants.LDAPFilterEnabledUsersOnly); } + + // A LDAP connection may have a custom filter + if (!String.IsNullOrWhiteSpace(ldapConnection?.CustomFilter)) + { + filter.Append(ldapConnection.CustomFilter); + } + filter.Append("(| "); // START OR // Fix bug https://github.com/Yvand/LDAPCP/issues/53 by escaping special characters with their hex representation as documented in https://ldap.com/ldap-filters/ - string input = Utils.EscapeSpecialCharacters(currentContext.Input); + string input = Utils.EscapeSpecialCharacters(inputText); - foreach (var ctConfig in currentContext.CurrentClaimTypeConfigList) + foreach (var ctConfig in claimTypeConfigList) { - filter.Append(AddLdapAttributeToFilter(currentContext, input, ctConfig)); + filter.Append(AddLdapAttributeToFilter(exactSearch, input, ctConfig)); } if (this.Settings.FilterEnabledUsersOnly) @@ -446,7 +458,7 @@ protected string BuildFilter(OperationContext currentContext) return filter.ToString(); } - protected string AddLdapAttributeToFilter(OperationContext currentContext, string input, ClaimTypeConfig attributeConfig) + protected string AddLdapAttributeToFilter(bool exactSearch, string input, ClaimTypeConfig attributeConfig) { // Prevent use of wildcard for LDAP attributes which do not support it if (String.Equals(attributeConfig.DirectoryObjectAttribute, "objectSid", StringComparison.InvariantCultureIgnoreCase)) @@ -460,7 +472,7 @@ protected string AddLdapAttributeToFilter(OperationContext currentContext, strin // Test if wildcard(s) should be added to the input string inputFormatted; - if (currentContext.ExactSearch || !attributeConfig.DirectoryObjectAttributeSupportsWildcard) + if (exactSearch || !attributeConfig.DirectoryObjectAttributeSupportsWildcard) { inputFormatted = input; } @@ -484,7 +496,7 @@ protected string AddLdapAttributeToFilter(OperationContext currentContext, strin return filter; } - protected List QueryLDAPServers(OperationContext currentContext, string ldapFilter) + protected List QueryLDAPServers(OperationContext currentContext) { if (this.Settings.LdapConnections == null || this.Settings.LdapConnections.Count == 0) { return null; } object lockResults = new object(); @@ -492,9 +504,15 @@ protected List QueryLDAPServers(OperationContext curre Stopwatch globalStopWatch = new Stopwatch(); globalStopWatch.Start(); + string ldapFilter = this.BuildFilter(currentContext.CurrentClaimTypeConfigList, currentContext.Input, currentContext.ExactSearch, null); //foreach (var ldapConnection in this.Settings.LdapConnections.Where(x => x.LdapEntry != null)) Parallel.ForEach(this.Settings.LdapConnections.Where(x => x.LdapEntry != null), ldapConnection => { + if (!String.IsNullOrWhiteSpace(ldapConnection.CustomFilter)) + { + // The LDAP filter needs to be entirely rewritten to include the filter specified in current connection + ldapFilter = this.BuildFilter(currentContext.CurrentClaimTypeConfigList, currentContext.Input, currentContext.ExactSearch, ldapConnection); + } Debug.WriteLine($"ldapConnection: Path: {ldapConnection.LdapEntry.Path}, UseDefaultADConnection: {ldapConnection.UseDefaultADConnection}"); Logger.LogDebug($"ldapConnection: Path: {ldapConnection.LdapEntry.Path}, UseDefaultADConnection: {ldapConnection.UseDefaultADConnection}"); using (DirectoryEntry directory = ldapConnection.LdapEntry) diff --git a/custom-claims-provider-samples/LDAPCPSE_basic/LDAPCPSE_Custom.cs b/custom-claims-provider-samples/LDAPCPSE_basic/LDAPCPSE_Custom.cs index 86eb028..bfa59f2 100644 --- a/custom-claims-provider-samples/LDAPCPSE_basic/LDAPCPSE_Custom.cs +++ b/custom-claims-provider-samples/LDAPCPSE_basic/LDAPCPSE_Custom.cs @@ -1,4 +1,5 @@ -using Yvand.LdapClaimsProvider; +using System; +using Yvand.LdapClaimsProvider; using Yvand.LdapClaimsProvider.Configuration; namespace LDAPCPSE_basic @@ -25,5 +26,15 @@ public override ILdapProviderSettings GetSettings() settings.EntityDisplayTextPrefix = "(custom) "; return settings; } + + public override void ValidateRuntimeSettings(OperationContext operationContext) + { + Uri currentSite = operationContext.UriContext; + string currentUser = operationContext.UserInHttpContext?.Value; + if (currentSite.Port == 6000) + { + operationContext.LdapConnections[0].CustomFilter = "(telephoneNumber=00110011)"; + } + } } }