Use LDAP support from DirectoryServices.Protocols for RBAC claim resolution on Linux for Negotiate (#25075)
This commit is contained in:
parent
c2f0331805
commit
098be5f5ee
|
|
@ -64,6 +64,7 @@ and are generated based on the last package release.
|
|||
<LatestPackageReference Include="System.ComponentModel.Annotations" />
|
||||
<LatestPackageReference Include="System.Diagnostics.DiagnosticSource" />
|
||||
<LatestPackageReference Include="System.Diagnostics.EventLog" />
|
||||
<LatestPackageReference Include="System.DirectoryServices.Protocols" />
|
||||
<LatestPackageReference Include="System.Drawing.Common" />
|
||||
<LatestPackageReference Include="System.IO.Pipelines" />
|
||||
<LatestPackageReference Include="System.Net.Http" />
|
||||
|
|
|
|||
|
|
@ -205,6 +205,10 @@
|
|||
<Uri>https://github.com/dotnet/runtime</Uri>
|
||||
<Sha>f4e99f4afa445b519abcd7c5c87cbf54771614db</Sha>
|
||||
</Dependency>
|
||||
<Dependency Name="System.DirectoryServices.Protocols" Version="5.0.0-rc.1.20425.1">
|
||||
<Uri>https://github.com/dotnet/runtime</Uri>
|
||||
<Sha>f4e99f4afa445b519abcd7c5c87cbf54771614db</Sha>
|
||||
</Dependency>
|
||||
<Dependency Name="System.Drawing.Common" Version="5.0.0-rc.1.20425.1">
|
||||
<Uri>https://github.com/dotnet/runtime</Uri>
|
||||
<Sha>f4e99f4afa445b519abcd7c5c87cbf54771614db</Sha>
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@
|
|||
<SystemComponentModelAnnotationsPackageVersion>5.0.0-rc.1.20425.1</SystemComponentModelAnnotationsPackageVersion>
|
||||
<SystemDiagnosticsDiagnosticSourcePackageVersion>5.0.0-rc.1.20425.1</SystemDiagnosticsDiagnosticSourcePackageVersion>
|
||||
<SystemDiagnosticsEventLogPackageVersion>5.0.0-rc.1.20425.1</SystemDiagnosticsEventLogPackageVersion>
|
||||
<SystemDirectoryServicesProtocolsPackageVersion>5.0.0-rc.1.20425.1</SystemDirectoryServicesProtocolsPackageVersion>
|
||||
<SystemDrawingCommonPackageVersion>5.0.0-rc.1.20425.1</SystemDrawingCommonPackageVersion>
|
||||
<SystemIOPipelinesPackageVersion>5.0.0-rc.1.20425.1</SystemIOPipelinesPackageVersion>
|
||||
<SystemNetHttpJsonPackageVersion>5.0.0-rc.1.20425.1</SystemNetHttpJsonPackageVersion>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication.Negotiate;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
|
@ -22,6 +23,23 @@ namespace NegotiateAuthSample
|
|||
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
|
||||
.AddNegotiate(options =>
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
/*
|
||||
options.EnableLdap("DOMAIN.net");
|
||||
|
||||
options.EnableLdap(settings =>
|
||||
{
|
||||
// Mandatory settings
|
||||
settings.Domain = "DOMAIN.com";
|
||||
// Optional settings
|
||||
settings.MachineAccountName = "machineName";
|
||||
settings.MachineAccountPassword = "PassW0rd";
|
||||
settings.IgnoreNestedGroups = true;
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
options.Events = new NegotiateEvents()
|
||||
{
|
||||
OnAuthenticationFailed = context =>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authentication.Negotiate
|
||||
{
|
||||
/// <summary>
|
||||
/// State for the RetrieveLdapClaims event.
|
||||
/// </summary>
|
||||
public class LdapContext : ResultContext<NegotiateOptions>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="LdapContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="scheme"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="settings"></param>
|
||||
public LdapContext(
|
||||
HttpContext context,
|
||||
AuthenticationScheme scheme,
|
||||
NegotiateOptions options,
|
||||
LdapSettings settings)
|
||||
: base(context, scheme, options)
|
||||
{
|
||||
LdapSettings = settings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The LDAP settings to use for the RetrieveLdapClaims event.
|
||||
/// </summary>
|
||||
public LdapSettings LdapSettings { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,12 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
|
|||
/// </summary>
|
||||
public Func<AuthenticationFailedContext, Task> OnAuthenticationFailed { get; set; } = context => Task.CompletedTask;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after the authentication before ClaimsIdentity is populated with claims retrieved through the LDAP connection.
|
||||
/// This event is invoked when <see cref="LdapSettings.EnableLdapClaimResolution"/> is set to true on <see cref="LdapSettings"/>.
|
||||
/// </summary>
|
||||
public Func<LdapContext, Task> OnRetrieveLdapClaims { get; set; } = context => Task.CompletedTask;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after the authentication is complete and a ClaimsIdentity has been generated.
|
||||
/// </summary>
|
||||
|
|
@ -31,6 +37,11 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
|
|||
/// </summary>
|
||||
public virtual Task AuthenticationFailed(AuthenticationFailedContext context) => OnAuthenticationFailed(context);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after the authentication before ClaimsIdentity is populated with claims retrieved through the LDAP connection.
|
||||
/// </summary>
|
||||
public virtual Task RetrieveLdapClaims(LdapContext context) => OnRetrieveLdapClaims(context);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after the authentication is complete and a ClaimsIdentity has been generated.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.DirectoryServices.Protocols;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authentication.Negotiate
|
||||
{
|
||||
internal static class LdapAdapter
|
||||
{
|
||||
public static async Task RetrieveClaimsAsync(LdapSettings settings, ClaimsIdentity identity, ILogger logger)
|
||||
{
|
||||
var user = identity.Name;
|
||||
var userAccountName = user.Substring(0, user.IndexOf('@'));
|
||||
var distinguishedName = settings.Domain.Split('.').Select(name => $"dc={name}").Aggregate((a, b) => $"{a},{b}");
|
||||
|
||||
var filter = $"(&(objectClass=user)(sAMAccountName={userAccountName}))"; // This is using ldap search query language, it is looking on the server for someUser
|
||||
var searchRequest = new SearchRequest(distinguishedName, filter, SearchScope.Subtree, null);
|
||||
var searchResponse = (SearchResponse) await Task<DirectoryResponse>.Factory.FromAsync(
|
||||
settings.LdapConnection.BeginSendRequest,
|
||||
settings.LdapConnection.EndSendRequest,
|
||||
searchRequest,
|
||||
PartialResultProcessing.NoPartialResultSupport,
|
||||
null);
|
||||
|
||||
if (searchResponse.Entries.Count > 0)
|
||||
{
|
||||
if (searchResponse.Entries.Count > 1)
|
||||
{
|
||||
logger.LogWarning($"More than one response received for query: {filter} with distinguished name: {distinguishedName}");
|
||||
}
|
||||
|
||||
var userFound = searchResponse.Entries[0]; //Get the object that was found on ldap
|
||||
var memberof = userFound.Attributes["memberof"]; // You can access ldap Attributes with Attributes property
|
||||
|
||||
foreach (var group in memberof)
|
||||
{
|
||||
// Example distinguished name: CN=TestGroup,DC=KERB,DC=local
|
||||
var groupDN = $"{Encoding.UTF8.GetString((byte[])group)}";
|
||||
var groupCN = groupDN.Split(',')[0].Substring("CN=".Length);
|
||||
|
||||
if (!settings.IgnoreNestedGroups)
|
||||
{
|
||||
GetNestedGroups(settings.LdapConnection, identity, distinguishedName, groupCN, logger);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddRole(identity, groupCN);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogWarning($"No response received for query: {filter} with distinguished name: {distinguishedName}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetNestedGroups(LdapConnection connection, ClaimsIdentity principal, string distinguishedName, string groupCN, ILogger logger)
|
||||
{
|
||||
var filter = $"(&(objectClass=group)(sAMAccountName={groupCN}))"; // This is using ldap search query language, it is looking on the server for someUser
|
||||
var searchRequest = new SearchRequest(distinguishedName, filter, System.DirectoryServices.Protocols.SearchScope.Subtree, null);
|
||||
var searchResponse = (SearchResponse)connection.SendRequest(searchRequest);
|
||||
|
||||
if (searchResponse.Entries.Count > 0)
|
||||
{
|
||||
if (searchResponse.Entries.Count > 1)
|
||||
{
|
||||
logger.LogWarning($"More than one response received for query: {filter} with distinguished name: {distinguishedName}");
|
||||
}
|
||||
|
||||
var group = searchResponse.Entries[0]; //Get the object that was found on ldap
|
||||
string name = group.DistinguishedName;
|
||||
AddRole(principal, name);
|
||||
|
||||
var memberof = group.Attributes["memberof"]; // You can access ldap Attributes with Attributes property
|
||||
if (memberof != null)
|
||||
{
|
||||
foreach (var member in memberof)
|
||||
{
|
||||
var groupDN = $"{Encoding.UTF8.GetString((byte[])member)}";
|
||||
var nestedGroupCN = groupDN.Split(',')[0].Substring("CN=".Length);
|
||||
GetNestedGroups(connection, principal, distinguishedName, nestedGroupCN, logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddRole(ClaimsIdentity identity, string role)
|
||||
{
|
||||
identity.AddClaim(new Claim(identity.RoleClaimType, role));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authentication.Negotiate.Internal
|
||||
{
|
||||
internal class NegotiateOptionsValidationStartupFilter : IStartupFilter
|
||||
{
|
||||
private readonly string _authenticationScheme;
|
||||
|
||||
public NegotiateOptionsValidationStartupFilter(string authenticationScheme)
|
||||
{
|
||||
_authenticationScheme = authenticationScheme;
|
||||
}
|
||||
|
||||
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
|
||||
{
|
||||
return builder =>
|
||||
{
|
||||
// Resolve NegotiateOptions on startup to trigger post configuration and bind LdapConnection if needed
|
||||
var options = builder.ApplicationServices.GetRequiredService<IOptionsMonitor<NegotiateOptions>>().Get(_authenticationScheme);
|
||||
next(builder);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.DirectoryServices.Protocols;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authentication.Negotiate
|
||||
{
|
||||
/// <summary>
|
||||
/// Options class for configuring LDAP connections on Linux
|
||||
/// </summary>
|
||||
public class LdapSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Configure whether LDAP connection should be used to resolve claims.
|
||||
/// This is mainly used on Linux.
|
||||
/// </summary>
|
||||
public bool EnableLdapClaimResolution { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The domain to use for the LDAP connection. This is a mandatory setting.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// DOMAIN.com
|
||||
/// </example>
|
||||
public string Domain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The machine account name to use when opening the LDAP connection.
|
||||
/// If this is not provided, the machine wide credentials of the
|
||||
/// domain joined machine will be used.
|
||||
/// </summary>
|
||||
public string MachineAccountName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The machine account password to use when opening the LDAP connection.
|
||||
/// This must be provided if a <see cref="MachineAccountName"/> is provided.
|
||||
/// </summary>
|
||||
public string MachineAccountPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This option indicates whether nested groups should be ignored when
|
||||
/// resolving Roles. The default is false.
|
||||
/// </summary>
|
||||
public bool IgnoreNestedGroups { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="LdapConnection"/> to be used to retrieve role claims.
|
||||
/// If no explicit connection is provided, an LDAP connection will be
|
||||
/// automatically created based on the <see cref="Domain"/>,
|
||||
/// <see cref="MachineAccountName"/> and <see cref="MachineAccountPassword"/>
|
||||
/// options. If provided, this connection will be used and the
|
||||
/// <see cref="Domain"/>, <see cref="MachineAccountName"/> and
|
||||
/// <see cref="MachineAccountPassword"/> options will not be used to create
|
||||
/// the <see cref="LdapConnection"/>.
|
||||
/// </summary>
|
||||
public LdapConnection LdapConnection { get; set; }
|
||||
|
||||
public void Validate()
|
||||
{
|
||||
if (EnableLdapClaimResolution)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Domain))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(EnableLdapClaimResolution)} is set to true but {nameof(Domain)} is not set.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(MachineAccountName) && !string.IsNullOrEmpty(MachineAccountPassword))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(MachineAccountPassword)} should only be specified when {nameof(MachineAccountName)} is configured.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,9 @@
|
|||
<ItemGroup>
|
||||
<Reference Include="Microsoft.AspNetCore.Authentication" />
|
||||
<Reference Include="Microsoft.AspNetCore.Connections.Abstractions" />
|
||||
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
|
||||
<Reference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" />
|
||||
<Reference Include="System.DirectoryServices.Protocols" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Negotiate;
|
||||
using Microsoft.AspNetCore.Authentication.Negotiate.Internal;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
|
|
@ -52,6 +54,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
public static AuthenticationBuilder AddNegotiate(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<NegotiateOptions> configureOptions)
|
||||
{
|
||||
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<NegotiateOptions>, PostConfigureNegotiateOptions>());
|
||||
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IStartupFilter>(new NegotiateOptionsValidationStartupFilter(authenticationScheme)));
|
||||
return builder.AddScheme<NegotiateOptions, NegotiateHandler>(authenticationScheme, displayName, configureOptions);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.Text.Encodings.Web;
|
||||
|
|
@ -324,10 +325,37 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
|
|||
user = new ClaimsPrincipal(new ClaimsIdentity(identity));
|
||||
}
|
||||
|
||||
var authenticatedContext = new AuthenticatedContext(Context, Scheme, Options)
|
||||
AuthenticatedContext authenticatedContext;
|
||||
|
||||
if (Options.LdapSettings.EnableLdapClaimResolution)
|
||||
{
|
||||
Principal = user
|
||||
};
|
||||
var ldapContext = new LdapContext(Context, Scheme, Options, Options.LdapSettings)
|
||||
{
|
||||
Principal = user
|
||||
};
|
||||
|
||||
await Events.RetrieveLdapClaims(ldapContext);
|
||||
|
||||
if (ldapContext.Result != null)
|
||||
{
|
||||
return ldapContext.Result;
|
||||
}
|
||||
|
||||
await LdapAdapter.RetrieveClaimsAsync(ldapContext.LdapSettings, ldapContext.Principal.Identity as ClaimsIdentity, Logger);
|
||||
|
||||
authenticatedContext = new AuthenticatedContext(Context, Scheme, Options)
|
||||
{
|
||||
Principal = ldapContext.Principal
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
authenticatedContext = new AuthenticatedContext(Context, Scheme, Options)
|
||||
{
|
||||
Principal = user
|
||||
};
|
||||
}
|
||||
|
||||
await Events.Authenticated(authenticatedContext);
|
||||
|
||||
if (authenticatedContext.Result != null)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authentication.Negotiate
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -33,6 +35,42 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
|
|||
/// </summary>
|
||||
public bool PersistNtlmCredentials { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration settings for LDAP connections used to retrieve claims.
|
||||
/// This should only be used on Linux systems.
|
||||
/// </summary>
|
||||
internal LdapSettings LdapSettings { get; } = new LdapSettings();
|
||||
|
||||
/// <summary>
|
||||
/// Use LDAP connections used to retrieve claims for the given domain.
|
||||
/// This should only be used on Linux systems.
|
||||
/// </summary>
|
||||
public void EnableLdap(string domain)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domain))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domain));
|
||||
}
|
||||
|
||||
LdapSettings.EnableLdapClaimResolution = true;
|
||||
LdapSettings.Domain = domain;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use LDAP connections used to retrieve claims using the configured settings.
|
||||
/// This should only be used on Linux systems.
|
||||
/// </summary>
|
||||
public void EnableLdap(Action<LdapSettings> configureSettings)
|
||||
{
|
||||
if (configureSettings == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configureSettings));
|
||||
}
|
||||
|
||||
LdapSettings.EnableLdapClaimResolution = true;
|
||||
configureSettings(LdapSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if integrated server Windows Auth is being used instead of this handler.
|
||||
/// See <see cref="PostConfigureNegotiateOptions"/>.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.DirectoryServices.Protocols;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -59,6 +61,36 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
|
|||
+ " Enable Windows Authentication for the server and the Negotiate Authentication handler will defer to it.");
|
||||
}
|
||||
}
|
||||
|
||||
var ldapSettings = options.LdapSettings;
|
||||
|
||||
if (ldapSettings.EnableLdapClaimResolution)
|
||||
{
|
||||
ldapSettings.Validate();
|
||||
|
||||
if (ldapSettings.LdapConnection == null)
|
||||
{
|
||||
var di = new LdapDirectoryIdentifier(server: ldapSettings.Domain, fullyQualifiedDnsHostName: true, connectionless: false);
|
||||
|
||||
if (string.IsNullOrEmpty(ldapSettings.MachineAccountName))
|
||||
{
|
||||
// Use default credentials
|
||||
ldapSettings.LdapConnection = new LdapConnection(di);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use specific specific machine account
|
||||
var machineAccount = ldapSettings.MachineAccountName + "@" + ldapSettings.Domain;
|
||||
var credentials = new NetworkCredential(machineAccount, ldapSettings.MachineAccountPassword);
|
||||
ldapSettings.LdapConnection = new LdapConnection(di, credentials);
|
||||
}
|
||||
|
||||
ldapSettings.LdapConnection.SessionOptions.ProtocolVersion = 3; //Setting LDAP Protocol to latest version
|
||||
ldapSettings.LdapConnection.Timeout = TimeSpan.FromMinutes(1);
|
||||
}
|
||||
|
||||
ldapSettings.LdapConnection.Bind(); // This line actually makes the connection.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
|
@ -13,7 +12,6 @@ using Microsoft.AspNetCore.Hosting;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
|
@ -371,6 +369,27 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
|
|||
Assert.Equal(1, callCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OnRetrieveLdapClaims_DoesNotFireWhenLdapDisabled()
|
||||
{
|
||||
var callCount = 0;
|
||||
using var host = await CreateHostAsync(options =>
|
||||
{
|
||||
options.Events = new NegotiateEvents()
|
||||
{
|
||||
OnRetrieveLdapClaims = context =>
|
||||
{
|
||||
callCount++;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
});
|
||||
var server = host.GetTestServer();
|
||||
|
||||
await KerberosStage1And2Auth(server, new TestConnection());
|
||||
Assert.Equal(0, callCount);
|
||||
}
|
||||
|
||||
private static async Task KerberosStage1And2Auth(TestServer server, TestConnection testConnection)
|
||||
{
|
||||
await KerberosStage1Auth(server, testConnection);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authentication.Negotiate.Test
|
||||
{
|
||||
public class LdapSettingsValidationTests
|
||||
{
|
||||
[Fact]
|
||||
public void EnabledWithoutDomainThrows()
|
||||
{
|
||||
var settings = new LdapSettings
|
||||
{
|
||||
EnableLdapClaimResolution = true
|
||||
};
|
||||
|
||||
Assert.Throws<ArgumentException>(() => settings.Validate());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AccountPasswordWithoutAccountNameThrows()
|
||||
{
|
||||
var settings = new LdapSettings
|
||||
{
|
||||
EnableLdapClaimResolution = true,
|
||||
MachineAccountPassword = "Passw0rd"
|
||||
};
|
||||
|
||||
Assert.Throws<ArgumentException>(() => settings.Validate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,8 +29,7 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate
|
|||
[Fact]
|
||||
public async Task ServerSupportsAuthButDisabled_Error()
|
||||
{
|
||||
using var host = await CreateHostAsync(supportsAuth: true, isEnabled: false);
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => host.Services.GetRequiredService<IOptions<NegotiateOptions>>().Value);
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () => await CreateHostAsync(supportsAuth: true, isEnabled: false));
|
||||
Assert.Equal("The Negotiate Authentication handler cannot be used on a server that directly supports Windows Authentication."
|
||||
+ " Enable Windows Authentication for the server and the Negotiate Authentication handler will defer to it.", ex.Message);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue