ec4c8fab87
Move configuration options from Core/DataAccess/DataSync/ExcelIO to dedicated Options folders within each project for better organization. Update all references and tests accordingly.
142 lines
5.4 KiB
C#
142 lines
5.4 KiB
C#
using JdeScoping.Infrastructure.Auth;
|
|
using JdeScoping.Infrastructure.Options;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using NSubstitute;
|
|
using Shouldly;
|
|
|
|
namespace JdeScoping.Infrastructure.Tests.Unit;
|
|
|
|
public class LdapAuthServiceTests
|
|
{
|
|
private readonly IOptions<LdapOptions> _ldapOptions;
|
|
private readonly ILogger<LdapAuthService> _logger;
|
|
|
|
public LdapAuthServiceTests()
|
|
{
|
|
_ldapOptions = Microsoft.Extensions.Options.Options.Create(new LdapOptions
|
|
{
|
|
ServerUrls = ["ldap.test.com"],
|
|
GroupDn = "CN=TestGroup,DC=test,DC=com",
|
|
SearchBase = "DC=test,DC=com",
|
|
ConnectionTimeoutSeconds = 5,
|
|
AdminBypassUsers = []
|
|
});
|
|
_logger = Substitute.For<ILogger<LdapAuthService>>();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AuthenticateAsync_EmptyUsername_ReturnsFailure()
|
|
{
|
|
// Arrange
|
|
var service = new LdapAuthService(_ldapOptions, _logger);
|
|
|
|
// Act
|
|
var result = await service.AuthenticateAsync("", "password");
|
|
|
|
// Assert
|
|
result.Success.ShouldBeFalse();
|
|
result.ErrorMessage.ShouldBe("Username and password are required");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AuthenticateAsync_EmptyPassword_ReturnsFailure()
|
|
{
|
|
// Arrange
|
|
var service = new LdapAuthService(_ldapOptions, _logger);
|
|
|
|
// Act
|
|
var result = await service.AuthenticateAsync("user", "");
|
|
|
|
// Assert
|
|
result.Success.ShouldBeFalse();
|
|
result.ErrorMessage.ShouldBe("Username and password are required");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AuthenticateAsync_NoServersConfigured_ReturnsConnectionError()
|
|
{
|
|
// Arrange
|
|
var emptyServerOptions = Microsoft.Extensions.Options.Options.Create(new LdapOptions
|
|
{
|
|
ServerUrls = [],
|
|
GroupDn = "CN=TestGroup,DC=test,DC=com",
|
|
SearchBase = "DC=test,DC=com",
|
|
AdminBypassUsers = []
|
|
});
|
|
var service = new LdapAuthService(emptyServerOptions, _logger);
|
|
|
|
// Act
|
|
var result = await service.AuthenticateAsync("user", "password");
|
|
|
|
// Assert
|
|
result.Success.ShouldBeFalse();
|
|
result.ErrorMessage.ShouldBe("Unable to connect to directory server");
|
|
}
|
|
|
|
[Fact]
|
|
public void GetUserInfoAsync_ThrowsNotSupportedException()
|
|
{
|
|
// Arrange
|
|
var service = new LdapAuthService(_ldapOptions, _logger);
|
|
|
|
// Act & Assert
|
|
Should.Throw<NotSupportedException>(() => service.GetUserInfoAsync("user").GetAwaiter().GetResult());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AuthenticateAsync_AdminBypassUser_ConfigurationIsRecognized()
|
|
{
|
|
// Arrange
|
|
// Note: We can't fully test admin bypass without a real LDAP server since bind still happens.
|
|
// This test verifies the configuration is recognized by checking that bypass users are configured.
|
|
var ldapOptionsWithBypass = Microsoft.Extensions.Options.Options.Create(new LdapOptions
|
|
{
|
|
ServerUrls = ["ldap.test.com"],
|
|
GroupDn = "CN=TestGroup,DC=test,DC=com",
|
|
SearchBase = "DC=test,DC=com",
|
|
ConnectionTimeoutSeconds = 5,
|
|
AdminBypassUsers = ["bypassuser", "adminuser"]
|
|
});
|
|
var service = new LdapAuthService(ldapOptionsWithBypass, _logger);
|
|
|
|
// Act - attempt to authenticate the bypass user (will fail LDAP connection, but config is exercised)
|
|
var result = await service.AuthenticateAsync("bypassuser", "anypassword");
|
|
|
|
// Assert - since we don't have a real LDAP server, connection will fail
|
|
// but the admin bypass configuration code path is exercised
|
|
result.Success.ShouldBeFalse();
|
|
// The error should be connection-related, not "Username and password are required"
|
|
result.ErrorMessage.ShouldNotBe("Username and password are required");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AuthenticateAsync_MultipleServersConfigured_TriesEachUntilAllFail()
|
|
{
|
|
// Arrange
|
|
var multiServerOptions = Microsoft.Extensions.Options.Options.Create(new LdapOptions
|
|
{
|
|
ServerUrls = ["ldap1.test.com", "ldap2.test.com", "ldap3.test.com"],
|
|
GroupDn = "CN=TestGroup,DC=test,DC=com",
|
|
SearchBase = "DC=test,DC=com",
|
|
ConnectionTimeoutSeconds = 1, // Fast timeout for test
|
|
AdminBypassUsers = []
|
|
});
|
|
var service = new LdapAuthService(multiServerOptions, _logger);
|
|
|
|
// Act
|
|
var result = await service.AuthenticateAsync("testuser", "testpassword");
|
|
|
|
// Assert - when all servers fail, authentication fails
|
|
// Note: Error message varies by platform - "Unable to connect to directory server" on Windows,
|
|
// "The feature is not supported." on macOS (where LDAP is not natively supported)
|
|
result.Success.ShouldBeFalse();
|
|
result.ErrorMessage.ShouldNotBeNullOrWhiteSpace();
|
|
// Verify it's not a validation error (which would indicate we didn't try the servers)
|
|
result.ErrorMessage.ShouldNotBe("Username and password are required");
|
|
}
|
|
|
|
// Note: Testing actual LDAP connections requires integration tests with a real/mock LDAP server
|
|
// These unit tests cover the basic validation and edge cases
|
|
}
|