| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758 |
- commit 552163ab77c269bc1f6c22bbf2b7560e0677c421
- Author: Justin Kotalik <[email protected]>
- Date: Fri Nov 10 17:02:31 2017 -0800
- Adds windows Auth support (#471)
- diff --git a/samples/NativeIISSample/Properties/launchSettings.json b/samples/NativeIISSample/Properties/launchSettings.json
- index 03665b8024c..ca62d6c6486 100644
- --- a/samples/NativeIISSample/Properties/launchSettings.json
- +++ b/samples/NativeIISSample/Properties/launchSettings.json
- @@ -1,6 +1,6 @@
- {
- "iisSettings": {
- - "windowsAuthentication": false,
- + "windowsAuthentication": true,
- "anonymousAuthentication": true,
- "iisExpress": {
- "applicationUrl": "http://localhost:5762/",
- diff --git a/samples/NativeIISSample/Startup.cs b/samples/NativeIISSample/Startup.cs
- index 85f6a5dd512..ef7bbad8ce6 100644
- --- a/samples/NativeIISSample/Startup.cs
- +++ b/samples/NativeIISSample/Startup.cs
- @@ -3,9 +3,11 @@
-
- using System;
- using System.Linq;
- +using Microsoft.AspNetCore.Authentication;
- using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.AspNetCore.Http;
- +using Microsoft.AspNetCore.Server.IISIntegration;
-
- namespace NativeIISSample
- {
- @@ -13,7 +15,7 @@ namespace NativeIISSample
- {
-
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- - public void Configure(IApplicationBuilder app, IHostingEnvironment env)
- + public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAuthenticationSchemeProvider authSchemeProvider)
- {
- app.Run(async (context) =>
- {
- @@ -38,8 +40,8 @@ namespace NativeIISSample
- await context.Response.WriteAsync(Environment.NewLine);
-
- await context.Response.WriteAsync("User: " + context.User.Identity.Name + Environment.NewLine);
- - //var scheme = await authSchemeProvider.GetSchemeAsync(IISDefaults.AuthenticationScheme);
- - //await context.Response.WriteAsync("DisplayName: " + scheme?.DisplayName + Environment.NewLine);
- + var scheme = await authSchemeProvider.GetSchemeAsync(IISDefaults.AuthenticationScheme);
- + await context.Response.WriteAsync("DisplayName: " + scheme?.DisplayName + Environment.NewLine);
-
- await context.Response.WriteAsync(Environment.NewLine);
-
- diff --git a/samples/NativeIISSample/applicationhost.config b/samples/NativeIISSample/applicationhost.config
- index b11f212ef2a..dd5daa04545 100644
- --- a/samples/NativeIISSample/applicationhost.config
- +++ b/samples/NativeIISSample/applicationhost.config
- @@ -299,12 +299,12 @@
- <application name="Active Server Pages" groupId="ASP" />
- </applicationDependencies>
- <authentication>
- - <anonymousAuthentication enabled="true" userName="" />
- + <anonymousAuthentication enabled="false" userName="" />
- <basicAuthentication enabled="false" />
- <clientCertificateMappingAuthentication enabled="false" />
- <digestAuthentication enabled="false" />
- <iisClientCertificateMappingAuthentication enabled="false"></iisClientCertificateMappingAuthentication>
- - <windowsAuthentication enabled="false">
- + <windowsAuthentication enabled="true">
- <providers>
- <add value="Negotiate" />
- <add value="NTLM" />
- diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/NativeMethods.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/NativeMethods.cs
- index 8015f9c0044..4d718793d67 100644
- --- a/src/Microsoft.AspNetCore.Server.IISIntegration/NativeMethods.cs
- +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/NativeMethods.cs
- @@ -75,7 +75,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- public unsafe static extern bool http_set_managed_context(IntPtr pHttpContext, IntPtr pvManagedContext);
-
- [DllImport(AspNetCoreModuleDll)]
- - public unsafe static extern int http_get_application_paths([MarshalAs(UnmanagedType.BStr)] out string fullPath, [MarshalAs(UnmanagedType.BStr)] out string virtualPath);
- + public unsafe static extern int http_get_application_properties(ref IISConfigurationData iiConfigData);
-
- [DllImport(AspNetCoreModuleDll)]
- public unsafe static extern bool http_shutdown();
- @@ -98,6 +98,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- [DllImport(AspNetCoreModuleDll)]
- internal unsafe static extern int http_response_set_known_header(IntPtr pHttpContext, int headerId, byte* pHeaderValue, ushort length, bool fReplace);
-
- + [DllImport(AspNetCoreModuleDll)]
- + public unsafe static extern int http_get_authentication_information(IntPtr pHttpContext, [MarshalAs(UnmanagedType.BStr)] out string authType, out IntPtr token);
- +
- [DllImport("kernel32.dll")]
- public static extern IntPtr GetModuleHandle(string lpModuleName);
-
- @@ -105,6 +108,5 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- {
- return GetModuleHandle(AspNetCoreModuleDll) != IntPtr.Zero;
- }
- -
- }
- }
- diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISConfigurationData.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISConfigurationData.cs
- new file mode 100644
- index 00000000000..76910ca9d39
- --- /dev/null
- +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISConfigurationData.cs
- @@ -0,0 +1,19 @@
- +// 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;
- +
- +namespace Microsoft.AspNetCore.Server.IISIntegration
- +{
- + [StructLayout(LayoutKind.Sequential)]
- + internal unsafe struct IISConfigurationData
- + {
- + [MarshalAs(UnmanagedType.BStr)]
- + public string pwzFullApplicationPath;
- + [MarshalAs(UnmanagedType.BStr)]
- + public string pwzVirtualApplicationPath;
- + public bool fWindowsAuthEnabled;
- + public bool fBasicAuthEnabled;
- + public bool fAnonymousAuthEnable;
- + }
- +}
- diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.FeatureCollection.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.FeatureCollection.cs
- index 27962f0c08f..2c2c9ef5fe0 100644
- --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.FeatureCollection.cs
- +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.FeatureCollection.cs
- @@ -7,10 +7,12 @@ using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Net;
- +using System.Security.Claims;
- using System.Threading;
- using System.Threading.Tasks;
- using Microsoft.AspNetCore.Http;
- using Microsoft.AspNetCore.Http.Features;
- +using Microsoft.AspNetCore.Http.Features.Authentication;
- using Microsoft.AspNetCore.WebUtilities;
- using Microsoft.Extensions.Primitives;
-
- @@ -22,7 +24,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- IHttpUpgradeFeature,
- IHttpConnectionFeature,
- IHttpRequestLifetimeFeature,
- - IHttpRequestIdentifierFeature
- + IHttpRequestIdentifierFeature,
- + IHttpAuthenticationFeature
- {
- // NOTE: When feature interfaces are added to or removed from this HttpProtocol implementation,
- // then the list of `implementedFeatures` in the generated code project MUST also be updated.
- @@ -223,6 +226,14 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- set => TraceIdentifier = value;
- }
-
- + ClaimsPrincipal IHttpAuthenticationFeature.User
- + {
- + get => User;
- + set => User = value;
- + }
- +
- + public IAuthenticationHandler Handler { get; set; }
- +
- object IFeatureCollection.this[Type key]
- {
- get => FastFeatureGet(key);
- diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.Features.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.Features.cs
- index 6b534405f5c..5ad7590adec 100644
- --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.Features.cs
- +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.Features.cs
- @@ -27,6 +27,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- private static readonly Type ISessionFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.ISessionFeature);
- private static readonly Type IHttpBodyControlFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpBodyControlFeature);
- private static readonly Type IHttpSendFileFeatureType = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature);
- + private static readonly Type IISHttpContextType = typeof(IISHttpContext);
-
- private object _currentIHttpRequestFeature;
- private object _currentIHttpResponseFeature;
- @@ -61,6 +62,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- _currentIHttpMinRequestBodyDataRateFeature = this;
- _currentIHttpMinResponseDataRateFeature = this;
- _currentIHttpBodyControlFeature = this;
- + _currentIHttpAuthenticationFeature = this;
- }
-
- internal object FastFeatureGet(Type key)
- @@ -133,6 +135,11 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- {
- return _currentIHttpSendFileFeature;
- }
- + if (key == IISHttpContextType)
- + {
- + return this;
- + }
- +
- return ExtraFeatureGet(key);
- }
-
- @@ -224,6 +231,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- {
- _currentIHttpSendFileFeature = feature;
- return;
- + }
- + if (key == IISHttpContextType)
- + {
- + throw new InvalidOperationException("Cannot set IISHttpContext in feature collection");
- };
- ExtraFeatureSet(key, feature);
- }
- diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.cs
- index a6520425e28..db83adc7cb2 100644
- --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.cs
- +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.cs
- @@ -10,9 +10,12 @@ using System.IO;
- using System.IO.Pipelines;
- using System.Net;
- using System.Runtime.InteropServices;
- +using System.Security.Claims;
- +using System.Security.Principal;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- +using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Http;
- using Microsoft.AspNetCore.HttpSys.Internal;
- using Microsoft.AspNetCore.WebUtilities;
- @@ -57,7 +60,11 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- private CurrentOperationType _currentOperationType;
- private Task _currentOperation = Task.CompletedTask;
-
- - internal unsafe IISHttpContext(PipeFactory pipeFactory, IntPtr pHttpContext)
- + private const string NtlmString = "NTLM";
- + private const string NegotiateString = "Negotiate";
- + private const string BasicString = "Basic";
- +
- + internal unsafe IISHttpContext(PipeFactory pipeFactory, IntPtr pHttpContext, IISOptions options)
- : base((HttpApiTypes.HTTP_REQUEST*)NativeMethods.http_get_raw_request(pHttpContext))
- {
- _thisHandle = GCHandle.Alloc(this);
- @@ -120,6 +127,15 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- HttpResponseHeaders = new HeaderCollection(); // TODO Optimize for known headers
- ResponseHeaders = HttpResponseHeaders;
-
- + if (options.ForwardWindowsAuthentication)
- + {
- + WindowsUser = GetWindowsPrincipal();
- + if (options.AutomaticAuthentication)
- + {
- + User = WindowsUser;
- + }
- + }
- +
- ResetFeatureCollection();
- }
-
- @@ -146,7 +162,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- public int LocalPort { get; set; }
- public string RequestConnectionId { get; set; }
- public string TraceIdentifier { get; set; }
- -
- + public ClaimsPrincipal User { get; set; }
- + internal WindowsPrincipal WindowsUser { get; set; }
- public Stream RequestBody { get; set; }
- public Stream ResponseBody { get; set; }
- public IPipe Input { get; set; }
- @@ -827,7 +844,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- // TODO: dispose managed state (managed objects).
- _thisHandle.Free();
- }
- -
- + if (WindowsUser?.Identity is WindowsIdentity wi)
- + {
- + wi.Dispose();
- + }
- disposedValue = true;
- }
- }
- @@ -851,5 +871,21 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- Write,
- Flush
- }
- +
- + private WindowsPrincipal GetWindowsPrincipal()
- + {
- + var hr = NativeMethods.http_get_authentication_information(_pHttpContext, out var authenticationType, out var token);
- +
- + if (hr == 0 && token != IntPtr.Zero && authenticationType != null)
- + {
- + if ((authenticationType.Equals(NtlmString, StringComparison.OrdinalIgnoreCase)
- + || authenticationType.Equals(NegotiateString, StringComparison.OrdinalIgnoreCase)
- + || authenticationType.Equals(BasicString, StringComparison.OrdinalIgnoreCase)))
- + {
- + return new WindowsPrincipal(new WindowsIdentity(token, authenticationType));
- + }
- + }
- + return null;
- + }
- }
- }
- diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContextOfT.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContextOfT.cs
- index 4fbd3b59563..1701122ec68 100644
- --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContextOfT.cs
- +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContextOfT.cs
- @@ -3,9 +3,10 @@
-
- using System;
- using System.Threading.Tasks;
- -using Microsoft.AspNetCore.Hosting.Server;
- using System.Threading;
- using System.IO.Pipelines;
- +using Microsoft.AspNetCore.Builder;
- +using Microsoft.AspNetCore.Hosting.Server;
-
- namespace Microsoft.AspNetCore.Server.IISIntegration
- {
- @@ -13,8 +14,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- {
- private readonly IHttpApplication<TContext> _application;
-
- - public IISHttpContextOfT(PipeFactory pipeFactory, IHttpApplication<TContext> application, IntPtr pHttpContext)
- - : base(pipeFactory, pHttpContext)
- + public IISHttpContextOfT(PipeFactory pipeFactory, IHttpApplication<TContext> application, IntPtr pHttpContext, IISOptions options)
- + : base(pipeFactory, pHttpContext, options)
- {
- _application = application;
- }
- diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpServer.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpServer.cs
- index 92602936136..4417e54c5b0 100644
- --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpServer.cs
- +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpServer.cs
- @@ -6,9 +6,12 @@ using System.IO.Pipelines;
- using System.Runtime.InteropServices;
- using System.Threading;
- using System.Threading.Tasks;
- +using Microsoft.AspNetCore.Authentication;
- +using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.AspNetCore.Hosting.Server;
- using Microsoft.AspNetCore.Http.Features;
- +using Microsoft.Extensions.Options;
-
- namespace Microsoft.AspNetCore.Server.IISIntegration
- {
- @@ -19,22 +22,30 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- private static NativeMethods.PFN_ASYNC_COMPLETION _onAsyncCompletion = OnAsyncCompletion;
-
- private IISContextFactory _iisContextFactory;
- -
- private PipeFactory _pipeFactory = new PipeFactory();
- private GCHandle _httpServerHandle;
- - private IApplicationLifetime _applicationLifetime;
- + private readonly IApplicationLifetime _applicationLifetime;
- + private readonly IAuthenticationSchemeProvider _authentication;
- + private readonly IISOptions _options;
-
- public IFeatureCollection Features { get; } = new FeatureCollection();
- - public IISHttpServer(IApplicationLifetime applicationLifetime)
- + public IISHttpServer(IApplicationLifetime applicationLifetime, IAuthenticationSchemeProvider authentication, IOptions<IISOptions> options)
- {
- _applicationLifetime = applicationLifetime;
- + _authentication = authentication;
- + _options = options.Value;
- +
- + if (_options.ForwardWindowsAuthentication)
- + {
- + authentication.AddScheme(new AuthenticationScheme(IISDefaults.AuthenticationScheme, _options.AuthenticationDisplayName, typeof(IISServerAuthenticationHandler)));
- + }
- }
-
- public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
- {
- _httpServerHandle = GCHandle.Alloc(this);
-
- - _iisContextFactory = new IISContextFactory<TContext>(_pipeFactory, application);
- + _iisContextFactory = new IISContextFactory<TContext>(_pipeFactory, application, _options);
-
- // Start the server by registering the callback
- NativeMethods.register_callbacks(_requestHandler, _shutdownHandler, _onAsyncCompletion, (IntPtr)_httpServerHandle, (IntPtr)_httpServerHandle);
- @@ -115,16 +126,18 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
- {
- private readonly IHttpApplication<T> _application;
- private readonly PipeFactory _pipeFactory;
- + private readonly IISOptions _options;
-
- - public IISContextFactory(PipeFactory pipeFactory, IHttpApplication<T> application)
- + public IISContextFactory(PipeFactory pipeFactory, IHttpApplication<T> application, IISOptions options)
- {
- _application = application;
- _pipeFactory = pipeFactory;
- + _options = options;
- }
-
- public IISHttpContext CreateHttpContext(IntPtr pHttpContext)
- {
- - return new IISHttpContextOfT<T>(_pipeFactory, _application, pHttpContext);
- + return new IISHttpContextOfT<T>(_pipeFactory, _application, pHttpContext, _options);
- }
- }
- }
- diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISServerAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISServerAuthenticationHandler.cs
- new file mode 100644
- index 00000000000..eba243f9f3d
- --- /dev/null
- +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISServerAuthenticationHandler.cs
- @@ -0,0 +1,59 @@
- +// 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.Security.Principal;
- +using System.Threading.Tasks;
- +using Microsoft.AspNetCore.Authentication;
- +using Microsoft.AspNetCore.Http;
- +
- +namespace Microsoft.AspNetCore.Server.IISIntegration
- +{
- + public class IISServerAuthenticationHandler : IAuthenticationHandler
- + {
- + private HttpContext _context;
- + private IISHttpContext _iisHttpContext;
- +
- + internal AuthenticationScheme Scheme { get; private set; }
- +
- + public Task<AuthenticateResult> AuthenticateAsync()
- + {
- + var user = _iisHttpContext.WindowsUser;
- + if (user != null && user.Identity != null && user.Identity.IsAuthenticated)
- + {
- + return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(user, Scheme.Name)));
- + }
- + else
- + {
- + return Task.FromResult(AuthenticateResult.NoResult());
- + }
- + }
- +
- + public Task ChallengeAsync(AuthenticationProperties properties)
- + {
- + // We would normally set the www-authenticate header here, but IIS does that for us.
- + _context.Response.StatusCode = 401;
- + return Task.CompletedTask;
- + }
- +
- + public Task ForbidAsync(AuthenticationProperties properties)
- + {
- + _context.Response.StatusCode = 403;
- + return Task.CompletedTask;
- + }
- +
- + public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
- + {
- + _iisHttpContext = context.Features.Get<IISHttpContext>();
- + if (_iisHttpContext == null)
- + {
- + throw new InvalidOperationException("No IISHttpContext found.");
- + }
- +
- + Scheme = scheme;
- + _context = context;
- +
- + return Task.CompletedTask;
- + }
- + }
- +}
- diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/WebHostBuilderIISExtensions.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/WebHostBuilderIISExtensions.cs
- index 8a92946ae14..89ecd567397 100644
- --- a/src/Microsoft.AspNetCore.Server.IISIntegration/WebHostBuilderIISExtensions.cs
- +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/WebHostBuilderIISExtensions.cs
- @@ -46,19 +46,27 @@ namespace Microsoft.AspNetCore.Hosting
- hostBuilder.CaptureStartupErrors(true);
-
- // TODO consider adding a configuration load where all variables needed are loaded from ANCM in one call.
- - var hResult = NativeMethods.http_get_application_paths(out var fullPath, out var virtualPath);
- + var iisConfigData = new IISConfigurationData();
- + var hResult = NativeMethods.http_get_application_properties(ref iisConfigData);
- +
- var exception = Marshal.GetExceptionForHR(hResult);
- if (exception != null)
- {
- throw exception;
- }
-
- - hostBuilder.UseContentRoot(fullPath);
- + hostBuilder.UseContentRoot(iisConfigData.pwzFullApplicationPath);
- return hostBuilder.ConfigureServices(services =>
- {
- services.AddSingleton<IServer, IISHttpServer>();
- - services.AddSingleton<IStartupFilter>(new IISServerSetupFilter(virtualPath));
- + services.AddSingleton<IStartupFilter>(new IISServerSetupFilter(iisConfigData.pwzVirtualApplicationPath));
- services.AddAuthenticationCore();
- + services.Configure<IISOptions>(
- + options =>
- + {
- + options.ForwardWindowsAuthentication = iisConfigData.fWindowsAuthEnabled || iisConfigData.fBasicAuthEnabled;
- + }
- + );
- });
- }
-
- diff --git a/test/IISIntegration.FunctionalTests/NtlmAuthentationTest.cs b/test/IISIntegration.FunctionalTests/NtlmAuthentationTest.cs
- index a61cd3f4474..20f2c65c9c4 100644
- --- a/test/IISIntegration.FunctionalTests/NtlmAuthentationTest.cs
- +++ b/test/IISIntegration.FunctionalTests/NtlmAuthentationTest.cs
- @@ -1,4 +1,4 @@
- -// Copyright (c) .NET Foundation. All rights reserved.
- +// 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.
-
- #if NET461
- diff --git a/test/IISIntegration.IISServerFunctionalTests/AuthenticationTests.cs b/test/IISIntegration.IISServerFunctionalTests/AuthenticationTests.cs
- new file mode 100644
- index 00000000000..d069b5211e9
- --- /dev/null
- +++ b/test/IISIntegration.IISServerFunctionalTests/AuthenticationTests.cs
- @@ -0,0 +1,121 @@
- +// 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.
- +
- +#if NET461
- +
- +using System;
- +using System.Collections.Generic;
- +using System.IO;
- +using System.Net;
- +using System.Net.Http;
- +using System.Text;
- +using System.Threading.Tasks;
- +using Microsoft.AspNetCore.Server.IIS.FunctionalTests;
- +using Microsoft.AspNetCore.Server.IntegrationTesting;
- +using Microsoft.Extensions.Logging;
- +using Microsoft.Extensions.Logging.Testing;
- +using Xunit;
- +using Xunit.Abstractions;
- +using Xunit.Sdk;
- +
- +namespace IISIntegration.IISServerFunctionalTests
- +{
- + public class AuthenticationTests : LoggedTest
- + {
- + public AuthenticationTests(ITestOutputHelper output) : base(output)
- + {
- + }
- +
- + [Fact(Skip = "See https://github.com/aspnet/IISIntegration/issues/424")]
- + public Task Authentication_InProcess_IISExpress()
- + {
- + return Authentication();
- + }
- +
- + private async Task Authentication()
- + {
- + var serverType = ServerType.IISExpress;
- + var architecture = RuntimeArchitecture.x64;
- + var testName = $"Authentication_{RuntimeFlavor.CoreClr}";
- + using (StartLog(out var loggerFactory, testName))
- + {
- + var logger = loggerFactory.CreateLogger("AuthenticationTest");
- +
- + var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), serverType, RuntimeFlavor.CoreClr, architecture)
- + {
- + ApplicationBaseUriHint = $"http://localhost:5051",
- + EnvironmentName = "Authentication", // Will pick the Start class named 'StartupAuthentication',
- + ServerConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("Http.config") : null,
- + SiteName = "HttpTestSite", // This is configured in the Http.config
- + TargetFramework = "netcoreapp2.0",
- + ApplicationType = ApplicationType.Portable
- + };
- +
- + using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
- + {
- + var deploymentResult = await deployer.DeployAsync();
- + var httpClient = deploymentResult.HttpClient;
- + deploymentResult.HttpClient.Timeout = TimeSpan.FromSeconds(5);
- +
- + // Request to base address and check if various parts of the body are rendered & measure the cold startup time.
- + var response = await RetryHelper.RetryRequest(() =>
- + {
- + return deploymentResult.HttpClient.GetAsync(string.Empty);
- + }, logger, deploymentResult.HostShutdownToken, retryCount: 30);
- +
- + var responseText = await response.Content.ReadAsStringAsync();
- + try
- + {
- + Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- + Assert.Equal("Hello World", responseText);
- +
- + response = await httpClient.GetAsync("/Anonymous");
- + responseText = await response.Content.ReadAsStringAsync();
- + Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- + Assert.Equal("Anonymous?True", responseText);
- +
- + response = await httpClient.GetAsync("/Restricted");
- + responseText = await response.Content.ReadAsStringAsync();
- + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
- + Assert.Contains("NTLM", response.Headers.WwwAuthenticate.ToString());
- + Assert.Contains("Negotiate", response.Headers.WwwAuthenticate.ToString());
- +
- + response = await httpClient.GetAsync("/RestrictedNTLM");
- + responseText = await response.Content.ReadAsStringAsync();
- + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
- + Assert.Contains("NTLM", response.Headers.WwwAuthenticate.ToString());
- + // Note we can't restrict a challenge to a specific auth type, the native auth modules always add themselves.
- + Assert.Contains("Negotiate", response.Headers.WwwAuthenticate.ToString());
- +
- + response = await httpClient.GetAsync("/Forbidden");
- + responseText = await response.Content.ReadAsStringAsync();
- + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
- +
- + var httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true };
- + httpClient = deploymentResult.CreateHttpClient(httpClientHandler);
- +
- + response = await httpClient.GetAsync("/Anonymous");
- + responseText = await response.Content.ReadAsStringAsync();
- + Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- + Assert.Equal("Anonymous?True", responseText);
- +
- + response = await httpClient.GetAsync("/Restricted");
- + responseText = await response.Content.ReadAsStringAsync();
- + Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- + Assert.NotEmpty(responseText);
- + }
- + catch (XunitException)
- + {
- + logger.LogWarning(response.ToString());
- + logger.LogWarning(responseText);
- + throw;
- + }
- + }
- + }
- + }
- + }
- +}
- +#elif NETCOREAPP2_0
- +#else
- +#error Target frameworks need to be updated
- +#endif
- diff --git a/test/IISIntegration.IISServerFunctionalTests/HelloWorldTests.cs b/test/IISIntegration.IISServerFunctionalTests/HelloWorldTests.cs
- index 1411934bf9e..610ac8c977d 100644
- --- a/test/IISIntegration.IISServerFunctionalTests/HelloWorldTests.cs
- +++ b/test/IISIntegration.IISServerFunctionalTests/HelloWorldTests.cs
- @@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
- {
- }
-
- - [Fact( Skip ="See https://github.com/aspnet/IISIntegration/issues/424")]
- + [Fact(Skip = "See https://github.com/aspnet/IISIntegration/issues/424")]
- public Task HelloWorld_InProcess_IISExpress_CoreClr_X64_Portable()
- {
- return HelloWorld(RuntimeFlavor.CoreClr, ApplicationType.Portable);
- diff --git a/test/IISIntegration.IISServerFunctionalTests/Http.config b/test/IISIntegration.IISServerFunctionalTests/Http.config
- index a92444f4538..c044b3294c8 100644
- --- a/test/IISIntegration.IISServerFunctionalTests/Http.config
- +++ b/test/IISIntegration.IISServerFunctionalTests/Http.config
- @@ -1026,4 +1026,14 @@
- </handlers>
- </system.webServer>
- </location>
- + <location path="HttpTestSite">
- + <system.webServer>
- + <security>
- + <authentication>
- + <anonymousAuthentication enabled="true" />
- + <windowsAuthentication enabled="true" />
- + </authentication>
- + </security>
- + </system.webServer>
- + </location>
- </configuration>
- diff --git a/test/IISTestSite/StartupAuthentication.cs b/test/IISTestSite/StartupAuthentication.cs
- new file mode 100644
- index 00000000000..1306332d1fb
- --- /dev/null
- +++ b/test/IISTestSite/StartupAuthentication.cs
- @@ -0,0 +1,80 @@
- +// 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.Security.Principal;
- +using Microsoft.AspNetCore.Authentication;
- +using Microsoft.AspNetCore.Builder;
- +using Microsoft.AspNetCore.Http;
- +using Microsoft.AspNetCore.Server.IISIntegration;
- +using Microsoft.Extensions.Logging;
- +using Xunit;
- +
- +namespace IISTestSite
- +{
- + public class StartupAuthentication
- + {
- + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
- + {
- + // Simple error page without depending on Diagnostics.
- + app.Use(async (context, next) =>
- + {
- + try
- + {
- + await next();
- + }
- + catch (Exception ex)
- + {
- + if (context.Response.HasStarted)
- + {
- + throw;
- + }
- +
- + context.Response.Clear();
- + context.Response.StatusCode = 500;
- + await context.Response.WriteAsync(ex.ToString());
- + }
- + });
- +
- + app.Use((context, next) =>
- + {
- + if (context.Request.Path.Equals("/Anonymous"))
- + {
- + return context.Response.WriteAsync("Anonymous?" + !context.User.Identity.IsAuthenticated);
- + }
- +
- + if (context.Request.Path.Equals("/Restricted"))
- + {
- + if (context.User.Identity.IsAuthenticated)
- + {
- + Assert.IsType<WindowsPrincipal>(context.User);
- + return context.Response.WriteAsync(context.User.Identity.AuthenticationType);
- + }
- + else
- + {
- + return context.ChallengeAsync(IISDefaults.AuthenticationScheme);
- + }
- + }
- +
- + if (context.Request.Path.Equals("/Forbidden"))
- + {
- + return context.ForbidAsync(IISDefaults.AuthenticationScheme);
- + }
- +
- + if (context.Request.Path.Equals("/RestrictedNTLM"))
- + {
- + if (string.Equals("NTLM", context.User.Identity.AuthenticationType, StringComparison.Ordinal))
- + {
- + return context.Response.WriteAsync("NTLM");
- + }
- + else
- + {
- + return context.ChallengeAsync(IISDefaults.AuthenticationScheme);
- + }
- + }
- +
- + return context.Response.WriteAsync("Hello World");
- + });
- + }
- + }
- +}
|