diff --git a/Bitai.Ldap.Helper.sln b/Bitai.Ldap.Helper.sln
index 2e4c7c0..01bbdf8 100644
--- a/Bitai.Ldap.Helper.sln
+++ b/Bitai.Ldap.Helper.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
-VisualStudioVersion = 18.6.11822.322 stable
+VisualStudioVersion = 18.6.11822.322
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bitai.LDAPHelper.DTO", "src\Bitai.LDAPHelper.DTO\Bitai.LDAPHelper.DTO.csproj", "{2386A126-F237-43FD-9A7C-FDF6718ACAD7}"
EndProject
@@ -24,6 +24,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C4F50587-D86
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bitai.LDAPHelper.Tests.Mocks", "tests\Bitai.LDAPHelper.Tests.Mocks\Bitai.LDAPHelper.Tests.Mocks.csproj", "{39BD48F5-8B64-469F-B6D7-9BB4582AE5F6}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bitai.LDAPHelper.LdapAdapters", "src\Bitai.LDAPHelper.LdapAdapters\Bitai.LDAPHelper.LdapAdapters.csproj", "{A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -94,6 +96,18 @@ Global
{39BD48F5-8B64-469F-B6D7-9BB4582AE5F6}.Release|x64.Build.0 = Release|Any CPU
{39BD48F5-8B64-469F-B6D7-9BB4582AE5F6}.Release|x86.ActiveCfg = Release|Any CPU
{39BD48F5-8B64-469F-B6D7-9BB4582AE5F6}.Release|x86.Build.0 = Release|Any CPU
+ {A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC}.Debug|x64.Build.0 = Debug|Any CPU
+ {A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC}.Debug|x86.Build.0 = Debug|Any CPU
+ {A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC}.Release|x64.ActiveCfg = Release|Any CPU
+ {A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC}.Release|x64.Build.0 = Release|Any CPU
+ {A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC}.Release|x86.ActiveCfg = Release|Any CPU
+ {A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -104,6 +118,7 @@ Global
{EF0E7C00-6116-46A7-BC21-6B30DE8361DD} = {D026C7BE-09AB-4BFD-B78E-1DF35734EBFD}
{2D343BE2-3906-4541-B9A5-CBB9A866BE54} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{39BD48F5-8B64-469F-B6D7-9BB4582AE5F6} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {A5F5FA73-059E-411F-BBF1-A8AC4C8B65BC} = {C4F50587-D862-43DF-AFE6-D2EEC0B7454D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DB0C2252-0846-4C49-970A-C7C68C82A4F3}
diff --git a/demo/Bitai.LDAPHelper.Demo/Bitai.LDAPHelper.Demo.csproj b/demo/Bitai.LDAPHelper.Demo/Bitai.LDAPHelper.Demo.csproj
index 7189504..c7bc7ce 100644
--- a/demo/Bitai.LDAPHelper.Demo/Bitai.LDAPHelper.Demo.csproj
+++ b/demo/Bitai.LDAPHelper.Demo/Bitai.LDAPHelper.Demo.csproj
@@ -19,7 +19,9 @@
+
+
diff --git a/demo/Bitai.LDAPHelper.Demo/Program.cs b/demo/Bitai.LDAPHelper.Demo/Program.cs
index ef9e2fd..1ca9190 100644
--- a/demo/Bitai.LDAPHelper.Demo/Program.cs
+++ b/demo/Bitai.LDAPHelper.Demo/Program.cs
@@ -1,13 +1,14 @@
-using System;
+using System;
using System.Threading.Tasks;
using Serilog;
using System.Linq;
using System.Collections.Generic;
-using Bitai.LDAPHelper.Demo.Mocks.LdapAdapters;
using Bitai.LDAPHelper.DTO;
using Microsoft.Extensions.Configuration;
-using Bitai.LDAPHelper.Demo.Mocks.LdapData;
using Bitai.LDAPHelper.LdapAdapters.Novell;
+using Bitai.LDAPHelper.Tests.Mocks.LdapAdapters;
+using Bitai.LDAPHelper.Tests.Mocks.LdapData;
+using Microsoft.Extensions.Logging;
namespace Bitai.LDAPHelper.Demo;
@@ -116,14 +117,20 @@ public static async Task Main(string[] args)
if (implementation == ImplementationType.Mock)
{
Log.Information("Initializing Mock Implementation...");
- _context.ConnectionFactory = new PersistentMockLdapConnectionFactoryAdapter();
-
+ _context.ConnectionFactory = new MockLdapPersistentConnectionFactoryAdapter();
+
+ // Create logger for MockLdapDataSeeder
+ using var loggerFactory = LoggerFactory.Create(builder => {
+ builder.ClearProviders();
+ builder.AddSerilog(Log.Logger);
+ });
+ var logger = loggerFactory.CreateLogger();
+
// Seed mock data
- var seeder = new MockDataSeeder(_context);
+ var seeder = new MockLdapDataSeeder(logger);
seeder.SeedAllData();
- //seeder.ExportGroupMembershipDataHierarchyToConsole(true);
- //Console.ReadKey();
+ seeder.PrintAllData();
// Set mock connection info (dummy values)
_context.SelectedLdapServer = "mock-server";
@@ -542,4 +549,4 @@ private static async Task AskToRunAgain()
await Main(new string[0]);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Bitai.LDAPHelper.LdapAdapters/Bitai.LDAPHelper.LdapAdapters.csproj b/src/Bitai.LDAPHelper.LdapAdapters/Bitai.LDAPHelper.LdapAdapters.csproj
new file mode 100644
index 0000000..7396c7a
--- /dev/null
+++ b/src/Bitai.LDAPHelper.LdapAdapters/Bitai.LDAPHelper.LdapAdapters.csproj
@@ -0,0 +1,33 @@
+
+
+
+ net10.0
+ enable
+ enable
+ Bitai.LDAPHelper.LdapAdapters
+ 10.0.0
+ Victor Bastidas (BITAI)
+ BITAI
+ LDAP Services Wrappers
+ Library to wrap an LDAP Service functionality.
+ © 2026 BITAI. MIT License.
+ https://github.com/bitai-cs/LDAPHelper
+ https://github.com/bitai-cs/LDAPHelper.git
+ ldap;authentication;authorization;directory;helper;oauth;openid;ad;security;identity
+ True
+ LICENSE.md
+ README.md
+
+
+
+
+ True
+ \
+
+
+ True
+ \
+
+
+
+
diff --git a/src/Bitai.LDAPHelper.LdapAdapters/Enums.cs b/src/Bitai.LDAPHelper.LdapAdapters/Enums.cs
new file mode 100644
index 0000000..b4a98aa
--- /dev/null
+++ b/src/Bitai.LDAPHelper.LdapAdapters/Enums.cs
@@ -0,0 +1,21 @@
+namespace Bitai.LDAPHelper.LdapAdapters;
+
+///
+/// LDAP modification operation types
+///
+public enum LdapModificationType
+{
+ Add = 0,
+ Delete = 1,
+ Replace = 2
+}
+
+///
+/// LDAP search scope values
+///
+public enum LdapSearchScope
+{
+ ScopeBase = 0,
+ ScopeOne = 1,
+ ScopeSub = 2
+}
diff --git a/src/Bitai.LDAPHelper.LdapAdapters/IConnectionInfo.cs b/src/Bitai.LDAPHelper.LdapAdapters/IConnectionInfo.cs
new file mode 100644
index 0000000..6805831
--- /dev/null
+++ b/src/Bitai.LDAPHelper.LdapAdapters/IConnectionInfo.cs
@@ -0,0 +1,9 @@
+namespace Bitai.LDAPHelper.LdapAdapters;
+
+public interface IConnectionInfo
+{
+ short ConnectionTimeout { get; }
+ string Server { get; }
+ int ServerPort { get; }
+ bool UseSSL { get; }
+}
diff --git a/src/Bitai.LDAPHelper/LdapAdapters/ILdapAttributeAdapter.cs b/src/Bitai.LDAPHelper.LdapAdapters/ILdapAttributeAdapter.cs
similarity index 100%
rename from src/Bitai.LDAPHelper/LdapAdapters/ILdapAttributeAdapter.cs
rename to src/Bitai.LDAPHelper.LdapAdapters/ILdapAttributeAdapter.cs
diff --git a/src/Bitai.LDAPHelper/LdapAdapters/ILdapAttributeSetAdapter.cs b/src/Bitai.LDAPHelper.LdapAdapters/ILdapAttributeSetAdapter.cs
similarity index 100%
rename from src/Bitai.LDAPHelper/LdapAdapters/ILdapAttributeSetAdapter.cs
rename to src/Bitai.LDAPHelper.LdapAdapters/ILdapAttributeSetAdapter.cs
diff --git a/src/Bitai.LDAPHelper/LdapAdapters/ILdapConnectionAdapter.cs b/src/Bitai.LDAPHelper.LdapAdapters/ILdapConnectionAdapter.cs
similarity index 87%
rename from src/Bitai.LDAPHelper/LdapAdapters/ILdapConnectionAdapter.cs
rename to src/Bitai.LDAPHelper.LdapAdapters/ILdapConnectionAdapter.cs
index 1350834..093bd97 100644
--- a/src/Bitai.LDAPHelper/LdapAdapters/ILdapConnectionAdapter.cs
+++ b/src/Bitai.LDAPHelper.LdapAdapters/ILdapConnectionAdapter.cs
@@ -16,11 +16,11 @@ public interface ILdapConnectionAdapter : IDisposable
void ServerCertificateValidationByPass();
Task ConnectAsync(string host, int port);
Task BindAsync(string userDN, string password);
- Task SearchAsync(SearchLimits searchLimits, string searchFilter, string[] attributeNames, bool typesOnly);
+ Task SearchAsync(ISearchLimits searchLimits, string searchFilter, string[] attributeNames, bool typesOnly);
ILdapAttributeSetAdapter CreateAttributeSet();
Task AddEntryAsync(string distinguishedName, ILdapAttributeSetAdapter attributes);
ILdapModificationAdapter CreateModification(LdapModificationType type, string attributeName, object value);
Task ModifyEntryAsync(string distinguishedName, IEnumerable modifications);
Task DeleteEntryAsync(string distinguishedName);
void Disconnect();
-}
\ No newline at end of file
+}
diff --git a/src/Bitai.LDAPHelper/LdapAdapters/ILdapConnectionFactoryAdapter.cs b/src/Bitai.LDAPHelper.LdapAdapters/ILdapConnectionFactoryAdapter.cs
similarity index 81%
rename from src/Bitai.LDAPHelper/LdapAdapters/ILdapConnectionFactoryAdapter.cs
rename to src/Bitai.LDAPHelper.LdapAdapters/ILdapConnectionFactoryAdapter.cs
index 2583143..cd0ed38 100644
--- a/src/Bitai.LDAPHelper/LdapAdapters/ILdapConnectionFactoryAdapter.cs
+++ b/src/Bitai.LDAPHelper.LdapAdapters/ILdapConnectionFactoryAdapter.cs
@@ -1,5 +1,3 @@
-using System.Threading.Tasks;
-
namespace Bitai.LDAPHelper.LdapAdapters;
///
@@ -8,8 +6,8 @@ namespace Bitai.LDAPHelper.LdapAdapters;
public interface ILdapConnectionFactoryAdapter
{
Task CreateConnectionAsync(
- ConnectionInfo connectionInfo,
+ IConnectionInfo connectionInfo,
string userAccount,
string password,
bool bindRequired = true);
-}
\ No newline at end of file
+}
diff --git a/src/Bitai.LDAPHelper/LdapAdapters/ILdapEntryAdapter.cs b/src/Bitai.LDAPHelper.LdapAdapters/ILdapEntryAdapter.cs
similarity index 100%
rename from src/Bitai.LDAPHelper/LdapAdapters/ILdapEntryAdapter.cs
rename to src/Bitai.LDAPHelper.LdapAdapters/ILdapEntryAdapter.cs
diff --git a/src/Bitai.LDAPHelper/LdapAdapters/ILdapMessageAdapter.cs b/src/Bitai.LDAPHelper.LdapAdapters/ILdapMessageAdapter.cs
similarity index 100%
rename from src/Bitai.LDAPHelper/LdapAdapters/ILdapMessageAdapter.cs
rename to src/Bitai.LDAPHelper.LdapAdapters/ILdapMessageAdapter.cs
diff --git a/src/Bitai.LDAPHelper/LdapAdapters/ILdapModificationAdapter.cs b/src/Bitai.LDAPHelper.LdapAdapters/ILdapModificationAdapter.cs
similarity index 100%
rename from src/Bitai.LDAPHelper/LdapAdapters/ILdapModificationAdapter.cs
rename to src/Bitai.LDAPHelper.LdapAdapters/ILdapModificationAdapter.cs
diff --git a/src/Bitai.LDAPHelper/LdapAdapters/ILdapSearchConstraintsAdapter.cs b/src/Bitai.LDAPHelper.LdapAdapters/ILdapSearchConstraintsAdapter.cs
similarity index 100%
rename from src/Bitai.LDAPHelper/LdapAdapters/ILdapSearchConstraintsAdapter.cs
rename to src/Bitai.LDAPHelper.LdapAdapters/ILdapSearchConstraintsAdapter.cs
diff --git a/src/Bitai.LDAPHelper/LdapAdapters/ILdapSearchQueueAdapter.cs b/src/Bitai.LDAPHelper.LdapAdapters/ILdapSearchQueueAdapter.cs
similarity index 100%
rename from src/Bitai.LDAPHelper/LdapAdapters/ILdapSearchQueueAdapter.cs
rename to src/Bitai.LDAPHelper.LdapAdapters/ILdapSearchQueueAdapter.cs
diff --git a/src/Bitai.LDAPHelper.LdapAdapters/ISearchLimits.cs b/src/Bitai.LDAPHelper.LdapAdapters/ISearchLimits.cs
new file mode 100644
index 0000000..281c880
--- /dev/null
+++ b/src/Bitai.LDAPHelper.LdapAdapters/ISearchLimits.cs
@@ -0,0 +1,9 @@
+namespace Bitai.LDAPHelper.LdapAdapters;
+
+public interface ISearchLimits
+{
+ string BaseDN { get; set; }
+ LdapSearchScope LdapSearchScope { get; set; }
+ int MaxSearchResults { get; set; }
+ int MaxSearchTimeout { get; set; }
+}
diff --git a/src/Bitai.LDAPHelper.LdapAdapters/LICENSE.md b/src/Bitai.LDAPHelper.LdapAdapters/LICENSE.md
new file mode 100644
index 0000000..e263634
--- /dev/null
+++ b/src/Bitai.LDAPHelper.LdapAdapters/LICENSE.md
@@ -0,0 +1,21 @@
+# MIT License
+
+Copyright (c) 2026 BITAI
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/Bitai.LDAPHelper.LdapAdapters/README.md b/src/Bitai.LDAPHelper.LdapAdapters/README.md
new file mode 100644
index 0000000..ee1b0f4
--- /dev/null
+++ b/src/Bitai.LDAPHelper.LdapAdapters/README.md
@@ -0,0 +1,110 @@
+# Bitai.LDAPHelper.LdapAdapters
+
+`Bitai.LDAPHelper.LdapAdapters` defines the LDAP adapter contracts used by the Bitai LDAP Helper libraries. It is a small abstraction layer that keeps the core helper logic independent from any specific LDAP client implementation, such as `Novell.Directory.Ldap.NETStandard`.
+
+This package contains interfaces and enums only. It does not open LDAP connections by itself and does not include a concrete LDAP provider.
+
+## Purpose
+
+Use this project when you need to:
+
+- Implement a concrete LDAP provider for `Bitai.LDAPHelper`.
+- Test LDAP-dependent code without binding directly to a real directory server.
+- Isolate application logic from vendor-specific LDAP client APIs.
+- Standardize LDAP operations such as bind, search, add, modify, delete, and attribute access.
+
+## Target Framework
+
+- .NET 10.0
+- Nullable reference types enabled
+- Implicit global usings enabled
+
+## Package Metadata
+
+- Package ID: `Bitai.LDAPHelper.LdapAdapters`
+- Version: `10.0.0`
+- Repository: `https://github.com/bitai-cs/LDAPHelper`
+- License: MIT
+
+## Main Contracts
+
+### Connection and Factory
+
+- `ILdapConnectionFactoryAdapter` creates configured LDAP connections from `IConnectionInfo` and credentials.
+- `ILdapConnectionAdapter` represents a connected LDAP session and exposes bind, search, add, modify, delete, disconnect, and dispose operations.
+- `IConnectionInfo` describes LDAP server address, port, SSL usage, and timeout settings.
+
+### Search
+
+- `ISearchLimits` describes base DN, search scope, result limits, and timeout limits.
+- `ILdapSearchQueueAdapter` abstracts queued LDAP search responses.
+- `ILdapMessageAdapter` distinguishes search result entries from search completion messages.
+
+### Entries and Attributes
+
+- `ILdapEntryAdapter` exposes an LDAP entry distinguished name and its attribute set.
+- `ILdapAttributeSetAdapter` creates and retrieves LDAP attributes by name.
+- `ILdapAttributeAdapter` exposes attribute values as string, string array, byte array, or byte array collection.
+
+### Modifications
+
+- `ILdapModificationAdapter` describes an LDAP attribute modification.
+- `LdapModificationType` supports add, delete, and replace operations.
+- `LdapSearchScope` supports base, one-level, and subtree LDAP searches.
+
+## Implementing an Adapter
+
+A concrete provider typically implements `ILdapConnectionFactoryAdapter` and `ILdapConnectionAdapter`, then maps the Bitai contracts to the provider-specific LDAP client.
+
+```csharp
+using Bitai.LDAPHelper.LdapAdapters;
+
+public sealed class CustomLdapConnectionFactoryAdapter : ILdapConnectionFactoryAdapter
+{
+ public async Task CreateConnectionAsync(
+ IConnectionInfo connectionInfo,
+ string userAccount,
+ string password,
+ bool bindRequired = true)
+ {
+ var connection = new CustomLdapConnectionAdapter();
+ connection.ConnectionTimeout = connectionInfo.ConnectionTimeout;
+ connection.SecureSocketLayer = connectionInfo.UseSSL;
+
+ await connection.ConnectAsync(connectionInfo.Server, connectionInfo.ServerPort);
+
+ if (bindRequired)
+ {
+ await connection.BindAsync(userAccount, password);
+ }
+
+ return connection;
+ }
+}
+```
+
+The adapter is then passed into higher-level `Bitai.LDAPHelper` services such as authentication, search, account management, and group membership validation.
+
+## Design Notes
+
+- The package intentionally contains no dependency on a specific LDAP SDK.
+- Adapter implementations should preserve provider exceptions only when callers can handle them meaningfully; otherwise, translate them into domain-level exceptions used by the consuming library.
+- `ILdapConnectionAdapter` implements `IDisposable`; concrete implementations should release network resources and provider-specific handles consistently.
+- `ServerCertificateValidationByPass` exists for compatibility with provider adapters. Use it only in controlled development or test environments.
+
+## Build
+
+From the repository root:
+
+```powershell
+dotnet build src/Bitai.LDAPHelper.LdapAdapters/Bitai.LDAPHelper.LdapAdapters.csproj
+```
+
+## Related Projects
+
+- `Bitai.LDAPHelper`: Core LDAP helper services that consume these contracts.
+- `Bitai.LDAPHelper.Tests.Mocks`: In-memory mock implementations for tests and demos.
+
+## License
+
+This project is licensed under the MIT License. See [LICENSE.md](LICENSE.md).
diff --git a/src/Bitai.LDAPHelper/Bitai.LDAPHelper.csproj b/src/Bitai.LDAPHelper/Bitai.LDAPHelper.csproj
index 833e4ce..785c96f 100644
--- a/src/Bitai.LDAPHelper/Bitai.LDAPHelper.csproj
+++ b/src/Bitai.LDAPHelper/Bitai.LDAPHelper.csproj
@@ -7,10 +7,10 @@
LDAP Services Wrappers
Library to wrap Novell.Directory.Ldap.NETStandard functionality to make LDAP common queries to search accounts and objects in a Directory Service.
© 2026 BITAI. All rights reserved.
- 10.0.0
- 10.0.0
+ 10.1.0
+ 10.1.0
hierarchy_32.png
- 10.0.0
+ 10.1.0
true
Bitai.LDAPHelper
Bitai.LDAPHelper
@@ -43,6 +43,7 @@
+
diff --git a/src/Bitai.LDAPHelper/ConnectionInfo.cs b/src/Bitai.LDAPHelper/ConnectionInfo.cs
index e66b8c4..0c76ed1 100644
--- a/src/Bitai.LDAPHelper/ConnectionInfo.cs
+++ b/src/Bitai.LDAPHelper/ConnectionInfo.cs
@@ -1,24 +1,26 @@
-namespace Bitai.LDAPHelper
+using Bitai.LDAPHelper.LdapAdapters;
+
+namespace Bitai.LDAPHelper
{
- public class ConnectionInfo
- {
- public ConnectionInfo(string server, int port, bool useSSL, short connectionTimeout)
- {
- Server = server;
- ServerPort = port;
- UseSSL = useSSL;
- ConnectionTimeout = connectionTimeout;
- }
+ public class ConnectionInfo : IConnectionInfo
+ {
+ public ConnectionInfo(string server, int port, bool useSSL, short connectionTimeout)
+ {
+ Server = server;
+ ServerPort = port;
+ UseSSL = useSSL;
+ ConnectionTimeout = connectionTimeout;
+ }
- public string Server { get; }
+ public string Server { get; }
- public int ServerPort { get; }
+ public int ServerPort { get; }
- public bool UseSSL { get; }
+ public bool UseSSL { get; }
- ///
- /// Connection timeout in seconds
- ///
- public short ConnectionTimeout { get; }
- }
+ ///
+ /// Connection timeout in seconds
+ ///
+ public short ConnectionTimeout { get; }
+ }
}
diff --git a/src/Bitai.LDAPHelper/Enums.cs b/src/Bitai.LDAPHelper/Enums.cs
index 55608a0..7d135a1 100644
--- a/src/Bitai.LDAPHelper/Enums.cs
+++ b/src/Bitai.LDAPHelper/Enums.cs
@@ -9,25 +9,5 @@ public enum LdapServerDefaultPorts : int
DefaultSslPort = 636,
DefaultGlobalCatalogPort = 3268,
DefaultGlobalCatalogSslPort = 3269
- }
-
- ///
- /// LDAP modification operation types
- ///
- public enum LdapModificationType
- {
- Add = 0,
- Delete = 1,
- Replace = 2
- }
-
- ///
- /// LDAP search scope values
- ///
- public enum LdapSearchScope
- {
- ScopeBase = 0,
- ScopeOne = 1,
- ScopeSub = 2
- }
-}
\ No newline at end of file
+ }
+}
diff --git a/src/Bitai.LDAPHelper/LdapAdapters/Novell/NovellLdapConnectionAdapter.cs b/src/Bitai.LDAPHelper/LdapAdapters/Novell/NovellLdapConnectionAdapter.cs
index 8f8bd63..0abe698 100644
--- a/src/Bitai.LDAPHelper/LdapAdapters/Novell/NovellLdapConnectionAdapter.cs
+++ b/src/Bitai.LDAPHelper/LdapAdapters/Novell/NovellLdapConnectionAdapter.cs
@@ -91,7 +91,7 @@ public async Task BindAsync(string userDN, string password) {
/// Whether to return only attribute types (no values).
/// Optional search constraints adapter which will be translated to the Novell constraints type.
/// A task that produces an wrapping the resulting .
- public async Task SearchAsync(SearchLimits searchLimits, string searchFilter, string[] attributeNames, bool typesOnly) {
+ public async Task SearchAsync(ISearchLimits searchLimits, string searchFilter, string[] attributeNames, bool typesOnly) {
var novellConstraints = new LdapSearchConstraints() {
ServerTimeLimit = searchLimits.MaxSearchTimeout,
MaxResults = searchLimits.MaxSearchResults
@@ -220,4 +220,4 @@ protected virtual void Dispose(bool disposing) {
_disposed = true;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Bitai.LDAPHelper/LdapAdapters/Novell/NovellLdapConnectionFactoryAdapter.cs b/src/Bitai.LDAPHelper/LdapAdapters/Novell/NovellLdapConnectionFactoryAdapter.cs
index d50fcb4..523406b 100644
--- a/src/Bitai.LDAPHelper/LdapAdapters/Novell/NovellLdapConnectionFactoryAdapter.cs
+++ b/src/Bitai.LDAPHelper/LdapAdapters/Novell/NovellLdapConnectionFactoryAdapter.cs
@@ -10,7 +10,7 @@ namespace Bitai.LDAPHelper.LdapAdapters.Novell;
public class NovellLdapConnectionFactoryAdapter : ILdapConnectionFactoryAdapter
{
public async Task CreateConnectionAsync(
- ConnectionInfo connectionInfo,
+ IConnectionInfo connectionInfo,
string userAccount,
string password,
bool bindRequired = true) {
@@ -40,4 +40,4 @@ public async Task CreateConnectionAsync(
return adapter;
}
-}
\ No newline at end of file
+}
diff --git a/src/Bitai.LDAPHelper/LdapAdapters/Novell/NovellLdapSearchQueueAdapter.cs b/src/Bitai.LDAPHelper/LdapAdapters/Novell/NovellLdapSearchQueueAdapter.cs
index 80b3f90..c5f4582 100644
--- a/src/Bitai.LDAPHelper/LdapAdapters/Novell/NovellLdapSearchQueueAdapter.cs
+++ b/src/Bitai.LDAPHelper/LdapAdapters/Novell/NovellLdapSearchQueueAdapter.cs
@@ -1,4 +1,4 @@
-using Novell.Directory.Ldap;
+using Novell.Directory.Ldap;
namespace Bitai.LDAPHelper.LdapAdapters.Novell;
@@ -17,4 +17,4 @@ public ILdapMessageAdapter GetResponse() {
LdapMessage message = _searchQueue.GetResponse();
return message != null ? new NovellLdapMessageAdapter(message) : null;
}
-}
\ No newline at end of file
+}
diff --git a/src/Bitai.LDAPHelper/SearchLimits.cs b/src/Bitai.LDAPHelper/SearchLimits.cs
index cf14ceb..a16bcf7 100644
--- a/src/Bitai.LDAPHelper/SearchLimits.cs
+++ b/src/Bitai.LDAPHelper/SearchLimits.cs
@@ -1,11 +1,13 @@
-namespace Bitai.LDAPHelper
+using Bitai.LDAPHelper.LdapAdapters;
+
+namespace Bitai.LDAPHelper
{
- public class SearchLimits
- {
- ///
- /// The base distinguished name (DN) to search from.
- ///
- public string BaseDN { get; set; } = "DC=com";
+ public class SearchLimits : ISearchLimits
+ {
+ ///
+ /// The base distinguished name (DN) to search from.
+ ///
+ public string BaseDN { get; set; } = "DC=com";
///
/// The scope of the search operation, which determines how deep the search will
@@ -20,14 +22,15 @@ public class SearchLimits
///
public int MaxSearchResults { get; set; } = 1000;
- ///
- /// Maximum number of seconds that the server waits when returning search results.
- /// The search operation will be terminated with an LdapException.TIME_LIMIT_EXCEEDED if the operation exceeds the time limit.
- ///
- public int MaxSearchTimeout { get; set; } = 60;
+ ///
+ /// Maximum number of seconds that the server waits when returning search results.
+ /// The search operation will be terminated with an LdapException.TIME_LIMIT_EXCEEDED if the operation exceeds the time limit.
+ ///
+ public int MaxSearchTimeout { get; set; } = 60;
- public SearchLimits(string baseDN) {
- this.BaseDN = baseDN;
- }
- }
+ public SearchLimits(string baseDN)
+ {
+ this.BaseDN = baseDN;
+ }
+ }
}
diff --git a/tests/Bitai.LDAPHelper.Tests.Mocks/Bitai.LDAPHelper.Tests.Mocks.csproj b/tests/Bitai.LDAPHelper.Tests.Mocks/Bitai.LDAPHelper.Tests.Mocks.csproj
index f554382..504b7be 100644
--- a/tests/Bitai.LDAPHelper.Tests.Mocks/Bitai.LDAPHelper.Tests.Mocks.csproj
+++ b/tests/Bitai.LDAPHelper.Tests.Mocks/Bitai.LDAPHelper.Tests.Mocks.csproj
@@ -4,18 +4,37 @@
net10.0
enable
enable
- 10.0.0
- 10.0.0
- 10.0.0
+ 10.1.0
+ 10.1.0
+ 10.1.0
Viko Bastidas (BITAI)
- © 2026 BITAI. All rights reserved.
+ © 2026 BITAI. MIT License.
https://github.com/bitai-cs/LDAPHelper.git
GitHub
- ldap, authentication, directory, helper
+ ldap;authentication;authorization;directory;helper;oauth;openid;ad;security;identity
+ Bitai.LDAPHelper.Tests.Mocks
+ BITAI
+ LDAP Services Wrappers
+ Mocks for testing BITAI.LDAPHelper library.
+ https://github.com/bitai-cs/LDAPHelper
+ True
+ LICENSE.md
+ README.md
+
+
+ True
+ \
+
+
+ True
+ \
+
+
+
diff --git a/tests/Bitai.LDAPHelper.Tests.Mocks/LICENSE.md b/tests/Bitai.LDAPHelper.Tests.Mocks/LICENSE.md
new file mode 100644
index 0000000..e263634
--- /dev/null
+++ b/tests/Bitai.LDAPHelper.Tests.Mocks/LICENSE.md
@@ -0,0 +1,21 @@
+# MIT License
+
+Copyright (c) 2026 BITAI
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapConnectionAdapter.cs b/tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapConnectionAdapter.cs
index df709d0..c57cf7f 100644
--- a/tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapConnectionAdapter.cs
+++ b/tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapConnectionAdapter.cs
@@ -40,7 +40,7 @@ public Task BindAsync(string userDN, string password) {
return Task.CompletedTask;
}
- public virtual Task SearchAsync(SearchLimits searchLimits, string searchFilter, string[] attributeNames, bool typesOnly) {
+ public virtual Task SearchAsync(ISearchLimits searchLimits, string searchFilter, string[] attributeNames, bool typesOnly) {
var matchingEntries = new List();
// Find matching results based on filter
@@ -114,4 +114,4 @@ protected virtual void Dispose(bool disposing) {
_disposed = true;
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapConnectionFactoryAdapter.cs b/tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapConnectionFactoryAdapter.cs
index 5839c9c..dd8ed70 100644
--- a/tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapConnectionFactoryAdapter.cs
+++ b/tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapConnectionFactoryAdapter.cs
@@ -11,7 +11,7 @@ public MockLdapConnectionFactoryAdapter(MockLdapConnectionAdapter connection) {
}
public async Task CreateConnectionAsync(
- ConnectionInfo connectionInfo,
+ IConnectionInfo connectionInfo,
string userAccount,
string password,
bool bindRequired = true) {
@@ -34,4 +34,4 @@ public async Task CreateConnectionAsync(
return (ILdapConnectionAdapter)_connection;
}
-}
\ No newline at end of file
+}
diff --git a/demo/Bitai.LDAPHelper.Demo/Mocks/LdapAdapters/PersistentMockLdapConnectionAdapter.cs b/tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapPersistentConnectionAdapter.cs
similarity index 95%
rename from demo/Bitai.LDAPHelper.Demo/Mocks/LdapAdapters/PersistentMockLdapConnectionAdapter.cs
rename to tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapPersistentConnectionAdapter.cs
index a330e13..a3cb143 100644
--- a/demo/Bitai.LDAPHelper.Demo/Mocks/LdapAdapters/PersistentMockLdapConnectionAdapter.cs
+++ b/tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapPersistentConnectionAdapter.cs
@@ -1,20 +1,16 @@
-using System;
-using System.Collections.Generic;
using System.Text;
-using System.Threading.Tasks;
using Bitai.LDAPHelper.LdapAdapters;
-using Bitai.LDAPHelper.Demo.Mocks.LdapData;
-using Bitai.LDAPHelper.Tests.Mocks.LdapAdapters;
+using Bitai.LDAPHelper.Tests.Mocks.LdapData;
-namespace Bitai.LDAPHelper.Demo.Mocks.LdapAdapters;
+namespace Bitai.LDAPHelper.Tests.Mocks.LdapAdapters;
-public class PersistentMockLdapConnectionAdapter : MockLdapConnectionAdapter
+public class MockLdapPersistentConnectionAdapter : MockLdapConnectionAdapter
{
- private readonly MockDataStore _dataStore;
+ private readonly MockLdapDataStore _dataStore;
- public PersistentMockLdapConnectionAdapter() : base()
+ public MockLdapPersistentConnectionAdapter() : base()
{
- _dataStore = MockDataStore.Instance;
+ _dataStore = MockLdapDataStore.Instance;
}
public override Task AddEntryAsync(string distinguishedName, ILdapAttributeSetAdapter attributes)
@@ -145,7 +141,7 @@ public override Task DeleteEntryAsync(string distinguishedName)
}
public override Task SearchAsync(
- SearchLimits searchLimits,
+ ISearchLimits searchLimits,
string searchFilter,
string[] attributeNames,
bool typesOnly)
diff --git a/demo/Bitai.LDAPHelper.Demo/Mocks/LdapAdapters/PersistentMockLdapConnectionFactoryAdapter.cs b/tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapPersistentConnectionFactoryAdapter.cs
similarity index 52%
rename from demo/Bitai.LDAPHelper.Demo/Mocks/LdapAdapters/PersistentMockLdapConnectionFactoryAdapter.cs
rename to tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapPersistentConnectionFactoryAdapter.cs
index b426ccc..c724fef 100644
--- a/demo/Bitai.LDAPHelper.Demo/Mocks/LdapAdapters/PersistentMockLdapConnectionFactoryAdapter.cs
+++ b/tests/Bitai.LDAPHelper.Tests.Mocks/LdapAdapters/MockLdapPersistentConnectionFactoryAdapter.cs
@@ -1,19 +1,18 @@
-using System.Threading.Tasks;
using Bitai.LDAPHelper.LdapAdapters;
-namespace Bitai.LDAPHelper.Demo.Mocks.LdapAdapters;
+namespace Bitai.LDAPHelper.Tests.Mocks.LdapAdapters;
-public class PersistentMockLdapConnectionFactoryAdapter : ILdapConnectionFactoryAdapter
+public class MockLdapPersistentConnectionFactoryAdapter : ILdapConnectionFactoryAdapter
{
- private readonly PersistentMockLdapConnectionAdapter _connection;
+ private readonly MockLdapPersistentConnectionAdapter _connection;
- public PersistentMockLdapConnectionFactoryAdapter()
+ public MockLdapPersistentConnectionFactoryAdapter()
{
- _connection = new PersistentMockLdapConnectionAdapter();
+ _connection = new MockLdapPersistentConnectionAdapter();
}
public Task CreateConnectionAsync(
- ConnectionInfo connectionInfo,
+ IConnectionInfo connectionInfo,
string userAccount,
string password,
bool bindRequired = true)
@@ -22,4 +21,4 @@ public Task CreateConnectionAsync(
_connection.BindAsync(userAccount, password);
return Task.FromResult(_connection);
}
-}
\ No newline at end of file
+}
diff --git a/demo/Bitai.LDAPHelper.Demo/Mocks/LdapData/MockDataSeeder.cs b/tests/Bitai.LDAPHelper.Tests.Mocks/LdapData/MockLdapDataSeeder.cs
similarity index 92%
rename from demo/Bitai.LDAPHelper.Demo/Mocks/LdapData/MockDataSeeder.cs
rename to tests/Bitai.LDAPHelper.Tests.Mocks/LdapData/MockLdapDataSeeder.cs
index 3facc56..1941c40 100644
--- a/demo/Bitai.LDAPHelper.Demo/Mocks/LdapData/MockDataSeeder.cs
+++ b/tests/Bitai.LDAPHelper.Tests.Mocks/LdapData/MockLdapDataSeeder.cs
@@ -1,17 +1,13 @@
-using Bitai.LDAPHelper.Tests.Mocks.LdapAdapters;
-using Serilog;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
+using Bitai.LDAPHelper.Tests.Mocks.LdapAdapters;
+using Microsoft.Extensions.Logging;
-namespace Bitai.LDAPHelper.Demo.Mocks.LdapData;
+namespace Bitai.LDAPHelper.Tests.Mocks.LdapData;
-public class MockDataSeeder
+public class MockLdapDataSeeder
{
- private readonly DemoContext _context;
- private readonly MockDataStore _dataStore;
+ private readonly MockLdapDataStore _dataStore;
private readonly Random _random;
+ private readonly ILogger _logger;
// Progressive RID counters (thread-safe)
private static int _nextUserRid = 1000; // Users start from 1000
@@ -19,20 +15,20 @@ public class MockDataSeeder
private static int _nextComputerRid = 3000; // Computers start from 3000
private static int _nextOtherRid = 4000; // Other objects start from 4000
- public MockDataSeeder(DemoContext context) {
- _context = context;
- _dataStore = MockDataStore.Instance;
+ public MockLdapDataSeeder(ILogger logger) {
+ _dataStore = MockLdapDataStore.Instance;
_random = new Random();
+ _logger = logger;
}
public void SeedAllData() {
_dataStore.Clear();
- Log.Information("=".PadRight(60, '='));
- Log.Information("MOCK DATA SEEDING STARTED");
- Log.Information("=".PadRight(60, '='));
- Log.Information($"RID counters initialized - Users: {_nextUserRid}, Groups: {_nextGroupRid}, Computers: {_nextComputerRid}");
- Log.Information("=".PadRight(60, '='));
+ _logger.LogInformation("=".PadRight(60, '='));
+ _logger.LogInformation("MOCK DATA SEEDING STARTED");
+ _logger.LogInformation("=".PadRight(60, '='));
+ _logger.LogInformation($"RID counters initialized - Users: {_nextUserRid}, Groups: {_nextGroupRid}, Computers: {_nextComputerRid}");
+ _logger.LogInformation("=".PadRight(60, '='));
// Create domain structure
SeedDomainStructure();
@@ -61,15 +57,25 @@ public void SeedAllData() {
// Create additional relationships
SeedAdditionalRelationships();
- Log.Information("=".PadRight(60, '='));
- Log.Information($"MOCK DATA SEEDING COMPLETED");
- Log.Information($"Total Entries Created: {_dataStore.Count}");
- Log.Information($"Users: {CountEntriesByObjectClass("user")}");
- Log.Information($"Groups: {CountEntriesByObjectClass("group")}");
- Log.Information($"Computers: {CountEntriesByObjectClass("computer")}");
- Log.Information($"OUs: {CountEntriesByObjectClass("organizationalUnit")}");
- Log.Information($"Final RID counters - Users: {_nextUserRid}, Groups: {_nextGroupRid}, Computers: {_nextComputerRid}");
- Log.Information("=".PadRight(60, '='));
+ _logger.LogInformation("=".PadRight(60, '='));
+ _logger.LogInformation($"MOCK DATA SEEDING COMPLETED");
+ _logger.LogInformation($"Total Entries Created: {_dataStore.Count}");
+ _logger.LogInformation($"Users: {CountEntriesByObjectClass("user")}");
+ _logger.LogInformation($"Groups: {CountEntriesByObjectClass("group")}");
+ _logger.LogInformation($"Computers: {CountEntriesByObjectClass("computer")}");
+ _logger.LogInformation($"OUs: {CountEntriesByObjectClass("organizationalUnit")}");
+ _logger.LogInformation($"Final RID counters - Users: {_nextUserRid}, Groups: {_nextGroupRid}, Computers: {_nextComputerRid}");
+ _logger.LogInformation("=".PadRight(60, '='));
+ }
+
+ public void PrintAllData() {
+ _logger.LogInformation("=".PadRight(60, '='));
+ _logger.LogInformation($"LISTING ALL ENTRIES: {_dataStore.Count}");
+ foreach (var entry in _dataStore.GetAllEntries())
+ {
+ _logger.LogInformation(entry.DistinguishedName);
+ }
+ _logger.LogInformation("=".PadRight(60, '='));
}
#region Domain Structure
@@ -196,16 +202,16 @@ private void SeedStandardUsers() {
}
private void SeedDemoSpecificUsers() {
- //// Create user for CreateUserAccount demo
- //var newUser = new UserData(
- // "Victor",
- // "Bastidas",
- // "victor.bastidas",
- // "HOLDING",
- // "OU=Seniors,OU=DevOps,OU=IT,DC=holding,DC=latam,DC=com",
- // "Senior DevOps Engineer"
- //);
- //CreateMockUser(newUser);
+ // Create user for CreateUserAccount demo
+ var newUser = new UserData(
+ "Victor",
+ "Bastidas",
+ "victor.bastidas",
+ "HOLDING",
+ "OU=Seniors,OU=DevOps,OU=IT,DC=holding,DC=latam,DC=com",
+ "Senior DevOps Engineer"
+ );
+ CreateMockUser(newUser);
// Create user for password reset demo
var passwordResetUser = new UserData(
@@ -295,7 +301,7 @@ private void CreateMockUser(UserData user) {
}
_dataStore.AddOrUpdateEntry(userEntry);
- Log.Debug($"Created user: {user.SAMAccountName} ({distinguishedName}) with RID: {rid}");
+ _logger.LogDebug($"Created user: {user.SAMAccountName} ({distinguishedName}) with RID: {rid}");
}
#endregion
@@ -378,7 +384,7 @@ private void CreateMockGroup(GroupData group) {
groupEntry.AddAttribute("distinguishedName", groupDistinguishedName);
_dataStore.AddOrUpdateEntry(groupEntry);
- Log.Debug($"Created group: {group.SAMAccountName} ({groupDistinguishedName}) with RID: {rid}");
+ _logger.LogDebug($"Created group: {group.SAMAccountName} ({groupDistinguishedName}) with RID: {rid}");
}
#endregion
@@ -438,7 +444,7 @@ private void CreateMockComputer(ComputerData computer) {
computerEntry.AddAttribute("distinguishedName", distinguishedName);
_dataStore.AddOrUpdateEntry(computerEntry);
- Log.Debug($"Created computer: {computer.Name} ({distinguishedName}) with RID: {rid}");
+ _logger.LogDebug($"Created computer: {computer.Name} ({distinguishedName}) with RID: {rid}");
}
#endregion
@@ -701,4 +707,4 @@ public ComputerData(string name, string containerDN, string description, string
}
#endregion
-}
\ No newline at end of file
+}
diff --git a/demo/Bitai.LDAPHelper.Demo/Mocks/LdapData/MockDataStore.cs b/tests/Bitai.LDAPHelper.Tests.Mocks/LdapData/MockLdapDataStore.cs
similarity index 84%
rename from demo/Bitai.LDAPHelper.Demo/Mocks/LdapData/MockDataStore.cs
rename to tests/Bitai.LDAPHelper.Tests.Mocks/LdapData/MockLdapDataStore.cs
index 4763c01..2b4c8b6 100644
--- a/demo/Bitai.LDAPHelper.Demo/Mocks/LdapData/MockDataStore.cs
+++ b/tests/Bitai.LDAPHelper.Tests.Mocks/LdapData/MockLdapDataStore.cs
@@ -1,20 +1,16 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
using Bitai.LDAPHelper.Tests.Mocks.LdapAdapters;
-namespace Bitai.LDAPHelper.Demo.Mocks.LdapData;
+namespace Bitai.LDAPHelper.Tests.Mocks.LdapData;
-public class MockDataStore
+public class MockLdapDataStore
{
- private static readonly Lazy _instance = new Lazy(() => new MockDataStore());
- public static MockDataStore Instance => _instance.Value;
+ private static readonly Lazy _instance = new Lazy(() => new MockLdapDataStore());
+ public static MockLdapDataStore Instance => _instance.Value;
private readonly Dictionary _entries;
private readonly ReaderWriterLockSlim _lock;
- private MockDataStore() {
+ private MockLdapDataStore() {
_entries = new Dictionary(StringComparer.OrdinalIgnoreCase);
_lock = new ReaderWriterLockSlim();
}
@@ -94,4 +90,4 @@ public int Count {
}
#endregion
-}
\ No newline at end of file
+}
diff --git a/tests/Bitai.LDAPHelper.Tests.Mocks/README.md b/tests/Bitai.LDAPHelper.Tests.Mocks/README.md
new file mode 100644
index 0000000..915142a
--- /dev/null
+++ b/tests/Bitai.LDAPHelper.Tests.Mocks/README.md
@@ -0,0 +1,146 @@
+# Bitai.LDAPHelper.Tests.Mocks
+
+`Bitai.LDAPHelper.Tests.Mocks` provides in-memory LDAP adapter implementations for testing and demo scenarios in the Bitai LDAP Helper ecosystem. It implements the adapter contracts consumed by `Bitai.LDAPHelper`, allowing authentication, search, account management, and group membership workflows to be exercised without a live LDAP or Active Directory server.
+
+This project is intended for tests, demos, and local development support. It should not be used as a production LDAP provider.
+
+## Purpose
+
+Use this project when you need to:
+
+- Unit test LDAP-dependent code without network access.
+- Verify how `Bitai.LDAPHelper` services call LDAP adapter contracts.
+- Seed predictable directory entries for repeatable test cases.
+- Record add, modify, and delete operations performed by higher-level services.
+- Run demo flows in an offline mock mode.
+
+## Target Framework
+
+- .NET 10.0
+- Nullable reference types enabled
+- Implicit global usings enabled
+
+## Package Metadata
+
+- Package ID: `Bitai.LDAPHelper.Tests.Mocks`
+- Version: `10.0.0`
+- Repository: `https://github.com/bitai-cs/LDAPHelper`
+- License: MIT
+
+## Project Dependencies
+
+This project references:
+
+- `Bitai.LDAPHelper`
+- `Bitai.LDAPHelper.LdapAdapters`
+- `Bitai.LDAPHelper.DTO`
+
+Those references provide the public helper services, adapter contracts, DTOs, and LDAP-related domain types used by the mocks.
+
+## Main Components
+
+### Adapter Mocks
+
+- `MockLdapConnectionAdapter` provides a configurable in-memory LDAP connection.
+- `MockLdapConnectionFactoryAdapter` returns a supplied mock connection and simulates connect and bind behavior.
+- `MockLdapPersistenConnectionAdapter` extends the basic mock connection with a shared in-memory data store.
+- `MockLdapPersistentConnectionFactoryAdapter` creates the persistent mock connection used by richer demo scenarios.
+- `MockLdapEntryAdapter`, `MockLdapAttributeSetAdapter`, `MockLdapAttributeAdapter`, `MockLdapMessageAdapter`, `MockLdapSearchQueueAdapter`, and `MockLdapModificationAdapter` implement the lower-level LDAP contracts.
+
+### Mock Data
+
+- `MockLdapDataStore` is a thread-safe singleton store keyed by distinguished name.
+- `MockLdapDataSeeder` populates the store with representative domains, organizational units, users, computers, groups, and group memberships.
+
+## Basic Unit Test Usage
+
+The simplest pattern is to create a mock connection, register search results for expected filters, and pass a mock factory into the service under test.
+
+```csharp
+using Bitai.LDAPHelper;
+using Bitai.LDAPHelper.DTO;
+using Bitai.LDAPHelper.Tests.Mocks.LdapAdapters;
+
+var connection = new MockLdapConnectionAdapter();
+
+var userEntry = new MockLdapEntryAdapter("CN=John Smith,OU=Users,DC=example,DC=com");
+userEntry.AddAttribute("cn", "John Smith");
+userEntry.AddAttribute("sAMAccountName", "john.smith");
+userEntry.AddAttribute("distinguishedName", userEntry.DistinguishedName);
+
+connection.AddSearchResult("(sAMAccountName=john.smith)", new List
+{
+ userEntry
+});
+
+var factory = new MockLdapConnectionFactoryAdapter(connection);
+
+var connectionInfo = new ConnectionInfo("localhost", 389, useSSL: false, connectionTimeout: 15);
+var credential = new LDAPDomainAccountCredential("EXAMPLE", "service.account", "StrongPassword!");
+var searchLimits = new SearchLimits("DC=example,DC=com")
+{
+ MaxSearchResults = 10,
+ MaxSearchTimeout = 15
+};
+
+var searcher = new Searcher(connectionInfo, searchLimits, credential, factory);
+```
+
+## Verifying Write Operations
+
+`MockLdapConnectionAdapter` records created, modified, and deleted entries so tests can assert side effects.
+
+```csharp
+var connection = new MockLdapConnectionAdapter();
+var factory = new MockLdapConnectionFactoryAdapter(connection);
+
+// Execute code that creates, modifies, or deletes LDAP entries.
+
+Assert.NotEmpty(connection.CreatedEntries);
+Assert.NotEmpty(connection.Modifications);
+Assert.NotEmpty(connection.DeletedEntries);
+```
+
+## Seeded Directory Usage
+
+For integration-style tests or demos, use the persistent connection factory with `MockLdapDataSeeder`.
+
+```csharp
+using Bitai.LDAPHelper.Tests.Mocks.LdapAdapters;
+using Bitai.LDAPHelper.Tests.Mocks.LdapData;
+using Microsoft.Extensions.Logging.Abstractions;
+
+var seeder = new MockLdapDataSeeder(NullLogger.Instance);
+seeder.SeedAllData();
+
+var factory = new MockLdapPersistentConnectionFactoryAdapter();
+```
+
+The seeded data includes representative LATAM domain roots, organizational units, user accounts, groups, computers, and nested memberships used by the repository tests and demo project.
+
+## Mock Behavior Notes
+
+- `MockLdapConnectionAdapter.ConnectAsync` rejects empty hosts, `unknown`, `0.0.0.0`, and non-positive ports.
+- `MockLdapConnectionAdapter.BindAsync` rejects empty credentials and intentionally weak or suspicious sample credentials used by tests.
+- Search matching in the basic mock connection is filter-pattern based; register the expected filter text before executing the service under test.
+- The persistent mock connection searches the shared `MockLdapDataStore` and applies a richer in-memory behavior model.
+- `ServerCertificateValidationByPass` throws in mock classes because certificate bypass behavior is not meaningful in an in-memory implementation.
+
+## Build
+
+From the repository root:
+
+```powershell
+dotnet build tests/Bitai.LDAPHelper.Tests.Mocks/Bitai.LDAPHelper.Tests.Mocks.csproj
+```
+
+## Related Projects
+
+- `Bitai.LDAPHelper.LdapAdapters`: Adapter contracts implemented by this project.
+- `Bitai.LDAPHelper`: Core LDAP helper services tested with these mocks.
+- `Bitai.LDAPHelper.Tests`: Test suite that consumes these mocks.
+- `Bitai.LDAPHelper.Demo`: Demo application that can run against the persistent mock data store.
+
+## License
+
+This project is licensed under the MIT License. See [LICENSE.md](LICENSE.md).