diff --git a/CHANGELOG.md b/CHANGELOG.md index 56cd64d..90eda79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## LDAPCP Second Edition v20.0 - Unreleased +* Add property CustomFilter to class DirectoryConnection, to allow setting a custom LDAP filter per LDAP connection +* Add sample custom claims provider LDAPCPSE_basic * Fix validation issue when multiple LDAP connections return an identical entity ## LDAPCP Second Edition v19.0.20240823.4 - Published in August 23, 2024 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 4f9a338..4b3f40b 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) { @@ -948,6 +952,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..91d935d 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,17 +496,23 @@ 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; } + if (currentContext.LdapConnections == null || currentContext.LdapConnections.Count == 0) { return null; } object lockResults = new object(); List results = new List(); Stopwatch globalStopWatch = new Stopwatch(); globalStopWatch.Start(); - //foreach (var ldapConnection in this.Settings.LdapConnections.Where(x => x.LdapEntry != null)) - Parallel.ForEach(this.Settings.LdapConnections.Where(x => x.LdapEntry != null), ldapConnection => + string ldapFilter = this.BuildFilter(currentContext.CurrentClaimTypeConfigList, currentContext.Input, currentContext.ExactSearch, null); + //foreach (var ldapConnection in currentContext.LdapConnections.Where(x => x.LdapEntry != null)) + Parallel.ForEach(currentContext.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/.gitignore b/custom-claims-provider-samples/.gitignore new file mode 100644 index 0000000..ea23616 --- /dev/null +++ b/custom-claims-provider-samples/.gitignore @@ -0,0 +1 @@ +*.dll \ No newline at end of file diff --git a/custom-claims-provider-samples/LDAPCPSE_basic/Features/LDAPCPSE_basic.ClaimsProvider/LDAPCPSE_basic.ClaimsProvider.EventReceiver.cs b/custom-claims-provider-samples/LDAPCPSE_basic/Features/LDAPCPSE_basic.ClaimsProvider/LDAPCPSE_basic.ClaimsProvider.EventReceiver.cs new file mode 100644 index 0000000..8df1ac5 --- /dev/null +++ b/custom-claims-provider-samples/LDAPCPSE_basic/Features/LDAPCPSE_basic.ClaimsProvider/LDAPCPSE_basic.ClaimsProvider.EventReceiver.cs @@ -0,0 +1,72 @@ +using Microsoft.SharePoint; +using Microsoft.SharePoint.Administration; +using Microsoft.SharePoint.Administration.Claims; +using System; +using System.Runtime.InteropServices; +using Yvand.LdapClaimsProvider.Logging; + +namespace LDAPCPSE_basic.Features +{ + /// + /// This class handles events raised during feature activation, deactivation, installation, uninstallation, and upgrade. + /// + /// + /// The GUID attached to this class may be used during packaging and should not be modified. + /// + + [Guid("8a0189bf-2e90-48b0-85f7-f639b42e3ef8")] + public class LDAPCPSECustomEventReceiver : SPClaimProviderFeatureReceiver + { + public override string ClaimProviderAssembly => typeof(LDAPCPSE_Custom).Assembly.FullName; + + public override string ClaimProviderDescription => LDAPCPSE_Custom.ClaimsProviderName; + + public override string ClaimProviderDisplayName => LDAPCPSE_Custom.ClaimsProviderName; + + public override string ClaimProviderType => typeof(LDAPCPSE_Custom).FullName; + + public override void FeatureActivated(SPFeatureReceiverProperties properties) + { + ExecBaseFeatureActivated(properties); + } + + private void ExecBaseFeatureActivated(Microsoft.SharePoint.SPFeatureReceiverProperties properties) + { + // Wrapper function for base FeatureActivated. + // Used because base keywork can lead to unverifiable code inside lambda expression + base.FeatureActivated(properties); + SPSecurity.RunWithElevatedPrivileges((SPSecurity.CodeToRunElevated)delegate () + { + try + { + Logger svc = Logger.Local; + Logger.Log($"[{LDAPCPSE_Custom.ClaimsProviderName}] Activating farm-scoped feature for claims provider \"{LDAPCPSE_Custom.ClaimsProviderName}\"", TraceSeverity.High, EventSeverity.Information, TraceCategory.Configuration); + } + catch (Exception ex) + { + Logger.LogException((string)LDAPCPSE_Custom.ClaimsProviderName, $"activating farm-scoped feature for claims provider \"{LDAPCPSE_Custom.ClaimsProviderName}\"", TraceCategory.Configuration, ex); + } + }); + } + + public override void FeatureUninstalling(SPFeatureReceiverProperties properties) + { + } + + public override void FeatureDeactivating(SPFeatureReceiverProperties properties) + { + SPSecurity.RunWithElevatedPrivileges((SPSecurity.CodeToRunElevated)delegate () + { + try + { + Logger.Log($"[{LDAPCPSE_Custom.ClaimsProviderName}] Deactivating farm-scoped feature for claims provider \"{LDAPCPSE_Custom.ClaimsProviderName}\": Removing claims provider from the farm (but not its configuration)", TraceSeverity.High, EventSeverity.Information, TraceCategory.Configuration); + base.RemoveClaimProvider((string)LDAPCPSE_Custom.ClaimsProviderName); + } + catch (Exception ex) + { + Logger.LogException((string)LDAPCPSE_Custom.ClaimsProviderName, $"deactivating farm-scoped feature for claims provider \"{LDAPCPSE_Custom.ClaimsProviderName}\"", TraceCategory.Configuration, ex); + } + }); + } + } +} \ No newline at end of file diff --git a/custom-claims-provider-samples/LDAPCPSE_basic/Features/LDAPCPSE_basic.ClaimsProvider/LDAPCPSE_basic.ClaimsProvider.Template.xml b/custom-claims-provider-samples/LDAPCPSE_basic/Features/LDAPCPSE_basic.ClaimsProvider/LDAPCPSE_basic.ClaimsProvider.Template.xml new file mode 100644 index 0000000..c27273d --- /dev/null +++ b/custom-claims-provider-samples/LDAPCPSE_basic/Features/LDAPCPSE_basic.ClaimsProvider/LDAPCPSE_basic.ClaimsProvider.Template.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/custom-claims-provider-samples/LDAPCPSE_basic/Features/LDAPCPSE_basic.ClaimsProvider/LDAPCPSE_basic.ClaimsProvider.feature b/custom-claims-provider-samples/LDAPCPSE_basic/Features/LDAPCPSE_basic.ClaimsProvider/LDAPCPSE_basic.ClaimsProvider.feature new file mode 100644 index 0000000..f4d85ff --- /dev/null +++ b/custom-claims-provider-samples/LDAPCPSE_basic/Features/LDAPCPSE_basic.ClaimsProvider/LDAPCPSE_basic.ClaimsProvider.feature @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/custom-claims-provider-samples/LDAPCPSE_basic/LDAPCPSE_Custom.cs b/custom-claims-provider-samples/LDAPCPSE_basic/LDAPCPSE_Custom.cs new file mode 100644 index 0000000..7bd85f8 --- /dev/null +++ b/custom-claims-provider-samples/LDAPCPSE_basic/LDAPCPSE_Custom.cs @@ -0,0 +1,49 @@ +using Microsoft.SharePoint.Administration; +using System; +using Yvand.LdapClaimsProvider; +using Yvand.LdapClaimsProvider.Configuration; +using Yvand.LdapClaimsProvider.Logging; + +namespace LDAPCPSE_basic +{ + public class LDAPCPSE_Custom : LDAPCPSE + { + /// + /// Sets the name of the claims provider, also set in (Get-SPTrustedIdentityTokenIssuer).ClaimProviderName property + /// + public new const string ClaimsProviderName = "LDAPCPSE_Custom"; + + /// + /// Do not remove or change this property + /// + public override string Name => ClaimsProviderName; + + public LDAPCPSE_Custom(string displayName) : base(displayName) + { + } + + public override ILdapProviderSettings GetSettings() + { + ClaimsProviderSettings settings = ClaimsProviderSettings.GetDefaultSettings(ClaimsProviderName); + settings.EntityDisplayTextPrefix = "(custom) "; + //settings.UserIdentifierClaimTypeConfig.DirectoryObjectAttributeForDisplayText = "displayName"; + return settings; + } + + public override void ValidateRuntimeSettings(OperationContext operationContext) + { + Uri currentSite = operationContext.UriContext; + string currentUser = operationContext.UserInHttpContext?.Value; + Logger.Log($"New request with input {operationContext.Input} from URL {currentSite} and user {currentUser}", TraceSeverity.High, EventSeverity.Information, TraceCategory.Custom); + // Returns all groups, or only users members of group testLdapcpGroup_002 + string customFilter = "(|(objectClass=group)(memberOf=CN=testLdapcpGroup_002,OU=ldapcp,DC=contoso,DC=local))"; + customFilter = "(|(&(objectClass=group)(|(sAMAccountName=testLdapcpGroup_002)(sAMAccountName=testLdapcpGroup_003)))(memberOf=CN=testLdapcpGroup_002,OU=ldapcp,DC=contoso,DC=local))"; + operationContext.LdapConnections[0].CustomFilter = customFilter; + //if (currentSite.Port == 6000) + //{ + // operationContext.LdapConnections[0].CustomFilter = "(telephoneNumber=00110011)"; + //} + Logger.Log($"Apply custom LDAP filter \"{customFilter}\"", TraceSeverity.High, EventSeverity.Information, TraceCategory.Custom); + } + } +} diff --git a/custom-claims-provider-samples/LDAPCPSE_basic/LDAPCPSE_basic.csproj b/custom-claims-provider-samples/LDAPCPSE_basic/LDAPCPSE_basic.csproj new file mode 100644 index 0000000..d5d2693 --- /dev/null +++ b/custom-claims-provider-samples/LDAPCPSE_basic/LDAPCPSE_basic.csproj @@ -0,0 +1,93 @@ + + + + Debug + AnyCPU + {CC278266-3F09-4908-BCE8-725D2AA9153E} + Library + Properties + LDAPCPSE_basic + LDAPCPSE_basic + v4.8 + 19.0 + 512 + {C1CDDADD-2546-481F-9697-4EA41081F2FC};{14822709-B5A1-4724-98CA-57A101D1B079};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + 14.1 + False + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + x64 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + true + + + key.snk + + + + + + False + + + False + ..\Yvand.LDAPCPSE.dll + + + + + LDAPCPSE_basic.ClaimsProvider.feature + + + + + + + {9492009a-e84b-4bcc-a960-e0671966464d} + + + + {034f2ce9-76ca-4b10-a136-85143ef303c9} + + + Package.package + + + + + + LDAPCPSE_basic.ClaimsProvider.feature + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64\gacutil.exe" /f /i "$(TargetPath)" +copy /Y "$(TargetDir)Yvand.LDAPCPSE.dll" $(ProjectDir)\bin + + \ No newline at end of file diff --git a/custom-claims-provider-samples/LDAPCPSE_basic/LDAPCPSE_basic.sln b/custom-claims-provider-samples/LDAPCPSE_basic/LDAPCPSE_basic.sln new file mode 100644 index 0000000..38b9df5 --- /dev/null +++ b/custom-claims-provider-samples/LDAPCPSE_basic/LDAPCPSE_basic.sln @@ -0,0 +1,27 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35013.160 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LDAPCPSE_basic", "LDAPCPSE_basic.csproj", "{CC278266-3F09-4908-BCE8-725D2AA9153E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CC278266-3F09-4908-BCE8-725D2AA9153E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC278266-3F09-4908-BCE8-725D2AA9153E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC278266-3F09-4908-BCE8-725D2AA9153E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {CC278266-3F09-4908-BCE8-725D2AA9153E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC278266-3F09-4908-BCE8-725D2AA9153E}.Release|Any CPU.Build.0 = Release|Any CPU + {CC278266-3F09-4908-BCE8-725D2AA9153E}.Release|Any CPU.Deploy.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {75E70229-6D16-4FD1-B776-1D3CF6994E65} + EndGlobalSection +EndGlobal diff --git a/custom-claims-provider-samples/LDAPCPSE_basic/Package/Package.Template.xml b/custom-claims-provider-samples/LDAPCPSE_basic/Package/Package.Template.xml new file mode 100644 index 0000000..640ff0f --- /dev/null +++ b/custom-claims-provider-samples/LDAPCPSE_basic/Package/Package.Template.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/custom-claims-provider-samples/LDAPCPSE_basic/Package/Package.package b/custom-claims-provider-samples/LDAPCPSE_basic/Package/Package.package new file mode 100644 index 0000000..4928ed3 --- /dev/null +++ b/custom-claims-provider-samples/LDAPCPSE_basic/Package/Package.package @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/custom-claims-provider-samples/LDAPCPSE_basic/Properties/AssemblyInfo.cs b/custom-claims-provider-samples/LDAPCPSE_basic/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..bebe4c0 --- /dev/null +++ b/custom-claims-provider-samples/LDAPCPSE_basic/Properties/AssemblyInfo.cs @@ -0,0 +1,38 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("EntraCP.Custom")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("EntraCP.Custom")] +[assembly: AssemblyCopyright("Copyright © 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("cc278266-3f09-4908-bce8-725d2aa9153e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/custom-claims-provider-samples/LDAPCPSE_basic/README.md b/custom-claims-provider-samples/LDAPCPSE_basic/README.md new file mode 100644 index 0000000..faace8b --- /dev/null +++ b/custom-claims-provider-samples/LDAPCPSE_basic/README.md @@ -0,0 +1,6 @@ +# Sample with a hard-coded configuration and a manual reference to LDAPCPSE + +This project shows how to create a claims provider that inherits LDAPCPSE. It uses a simple, hard-coded configuration to specify the tenant. + +> [!WARNING] +> Do NOT deploy this solution in a SharePoint farm that already has LDAPCPSE deployed, unless both use **exactly** the same version of `Yvand.LDAPCPSE.dll`. diff --git a/custom-claims-provider-samples/README.md b/custom-claims-provider-samples/README.md new file mode 100644 index 0000000..9c44320 --- /dev/null +++ b/custom-claims-provider-samples/README.md @@ -0,0 +1,4 @@ +# Sample projects for developers + +This folder contains Visual Studio projects that developers can use to create their custom clainms providers based on LDAPCPSE. +This is useful only for specific needs.