From 6004a0fb44c48a21ea1dab712c19076b3348588e Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 3 Jun 2020 17:47:32 +1000 Subject: [PATCH] wip starts migrating get remaining seconds, removes a bunch of code from netframework thats already been moved. --- .../BackOfficeCookieManagerTests.cs | 29 +- .../Controllers/AuthenticationController.cs | 28 +- ...coBackOfficeServiceCollectionExtensions.cs | 3 + .../Security/BackOfficeCookieManager.cs | 26 +- .../ConfigureBackOfficeCookieOptions.cs | 9 +- .../Extensions/LinkGeneratorExtensions.cs | 18 ++ .../Security/AppBuilderExtensions.cs | 262 +----------------- .../Security/BackOfficeCookieManager.cs | 120 -------- ...ForceRenewalCookieAuthenticationHandler.cs | 14 +- .../UmbracoBackOfficeCookieAuthOptions.cs | 28 -- src/Umbraco.Web/Umbraco.Web.csproj | 1 - src/Umbraco.Web/UmbracoDefaultOwinStartup.cs | 19 +- 12 files changed, 94 insertions(+), 463 deletions(-) rename src/{Umbraco.Tests/Security => Umbraco.Tests.Integration/Umbraco.Web.BackOffice.Security}/BackOfficeCookieManagerTests.cs (79%) delete mode 100644 src/Umbraco.Web/Security/BackOfficeCookieManager.cs diff --git a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice.Security/BackOfficeCookieManagerTests.cs similarity index 79% rename from src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs rename to src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice.Security/BackOfficeCookieManagerTests.cs index 1e451cf57d..f17f5eb83e 100644 --- a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Web.BackOffice.Security/BackOfficeCookieManagerTests.cs @@ -1,36 +1,23 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Linq; -using System.Web; -using Microsoft.Owin; -using Moq; + + using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web.Composing; -using Umbraco.Tests.Testing; -using Umbraco.Tests.Testing.Objects.Accessors; -using Umbraco.Web; -using Umbraco.Web.PublishedCache; -using Umbraco.Web.Routing; -using Umbraco.Web.Security; -using Umbraco.Tests.Common; +using Umbraco.Tests.Integration.Implementations; namespace Umbraco.Tests.Security { [TestFixture] - [UmbracoTest(WithApplication = true)] - public class BackOfficeCookieManagerTests : UmbracoTestBase + public class BackOfficeCookieManagerTests { [Test] public void ShouldAuthenticateRequest_When_Not_Configured() { + var testHelper = new TestHelper(); + //should force app ctx to show not-configured ConfigurationManager.AppSettings.Set(Constants.AppSettings.ConfigurationStatus, ""); - var httpContextAccessor = TestHelper.GetHttpContextAccessor(); - var globalSettings = TestObjects.GetGlobalSettings(); + var httpContextAccessor = testHelper.GetHttpContextAccessor(); + var globalSettings = testHelper.SettingsForTests.GetDefaultGlobalSettings(); var umbracoContext = new UmbracoContext( httpContextAccessor, Mock.Of(), diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index 03718cb1c4..ae720a8e35 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -7,10 +7,12 @@ using System.Threading.Tasks; using Umbraco.Core; using Umbraco.Core.BackOffice; using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; using Umbraco.Core.Mapping; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; using Umbraco.Extensions; +using Umbraco.Net; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Controllers; @@ -36,6 +38,8 @@ namespace Umbraco.Web.BackOffice.Controllers private readonly IUserService _userService; private readonly UmbracoMapper _umbracoMapper; private readonly IGlobalSettings _globalSettings; + private readonly ILogger _logger; + private readonly IIpResolver _ipResolver; // TODO: We need to import the logic from Umbraco.Web.Editors.AuthenticationController // TODO: We need to review all _userManager.Raise calls since many/most should be on the usermanager or signinmanager, very few should be here @@ -46,7 +50,8 @@ namespace Umbraco.Web.BackOffice.Controllers BackOfficeSignInManager signInManager, IUserService userService, UmbracoMapper umbracoMapper, - IGlobalSettings globalSettings) + IGlobalSettings globalSettings, + ILogger logger, IIpResolver ipResolver) { _umbracoContextAccessor = umbracoContextAccessor; _userManager = backOfficeUserManager; @@ -54,6 +59,27 @@ namespace Umbraco.Web.BackOffice.Controllers _userService = userService; _umbracoMapper = umbracoMapper; _globalSettings = globalSettings; + _logger = logger; + _ipResolver = ipResolver; + } + + [HttpGet] + public double GetRemainingTimeoutSeconds() + { + var backOfficeIdentity = HttpContext.User.GetUmbracoIdentity(); + var remainingSeconds = HttpContext.User.GetRemainingAuthSeconds(); + if (remainingSeconds <= 30 && backOfficeIdentity != null) + { + //NOTE: We are using 30 seconds because that is what is coded into angular to force logout to give some headway in + // the timeout process. + + _logger.Info( + "User logged will be logged out due to timeout: {Username}, IP Address: {IPAddress}", + backOfficeIdentity.Name, + _ipResolver.GetCurrentRequestIpAddress()); + } + + return remainingSeconds; } /// diff --git a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs index ef5a4bfa5f..4eb2afcbb5 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBackOfficeServiceCollectionExtensions.cs @@ -23,6 +23,9 @@ namespace Umbraco.Extensions { services.AddAntiforgery(); + // TODO: We had this check in v8 where we don't enable these unless we can run... + //if (runtimeState.Level != RuntimeLevel.Upgrade && runtimeState.Level != RuntimeLevel.Run) return app; + services .AddAuthentication(Constants.Security.BackOfficeAuthenticationType) .AddCookie(Constants.Security.BackOfficeAuthenticationType); diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeCookieManager.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeCookieManager.cs index ca71f8f8af..7af272e193 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeCookieManager.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeCookieManager.cs @@ -1,13 +1,16 @@ using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Routing; using System; using System.Collections.Generic; using System.Linq; using Umbraco.Core; +using Umbraco.Extensions; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Hosting; +using Umbraco.Web.BackOffice.Controllers; namespace Umbraco.Web.BackOffice.Security { @@ -30,11 +33,24 @@ namespace Umbraco.Web.BackOffice.Security private readonly string[] _explicitPaths; private readonly string _getRemainingSecondsPath; - public BackOfficeCookieManager(IUmbracoContextAccessor umbracoContextAccessor, IRuntimeState runtime, IHostingEnvironment hostingEnvironment, IGlobalSettings globalSettings, IRequestCache requestCache) - : this(umbracoContextAccessor, runtime, hostingEnvironment, globalSettings, requestCache, null) + public BackOfficeCookieManager( + IUmbracoContextAccessor umbracoContextAccessor, + IRuntimeState runtime, + IHostingEnvironment hostingEnvironment, + IGlobalSettings globalSettings, + IRequestCache requestCache, + LinkGenerator linkGenerator) + : this(umbracoContextAccessor, runtime, hostingEnvironment, globalSettings, requestCache, linkGenerator, null) { } - public BackOfficeCookieManager(IUmbracoContextAccessor umbracoContextAccessor, IRuntimeState runtime, IHostingEnvironment hostingEnvironment, IGlobalSettings globalSettings, IRequestCache requestCache, IEnumerable explicitPaths) + public BackOfficeCookieManager( + IUmbracoContextAccessor umbracoContextAccessor, + IRuntimeState runtime, + IHostingEnvironment hostingEnvironment, + IGlobalSettings globalSettings, + IRequestCache requestCache, + LinkGenerator linkGenerator, + IEnumerable explicitPaths) { _umbracoContextAccessor = umbracoContextAccessor; _runtime = runtime; @@ -42,9 +58,7 @@ namespace Umbraco.Web.BackOffice.Security _globalSettings = globalSettings; _requestCache = requestCache; _explicitPaths = explicitPaths?.ToArray(); - var backOfficePath = _globalSettings.GetBackOfficePath(_hostingEnvironment); - // TODO: We shouldn't hard code this path - _getRemainingSecondsPath = $"{backOfficePath}/backoffice/UmbracoApi/Authentication/GetRemainingTimeoutSeconds"; + _getRemainingSecondsPath = linkGenerator.GetUmbracoApiService(x => x.GetRemainingTimeoutSeconds()); } /// diff --git a/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs b/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs index fd009eab23..c81d8bd07c 100644 --- a/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs +++ b/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs @@ -18,6 +18,7 @@ using Umbraco.Core.Security; using Umbraco.Extensions; using Microsoft.Extensions.DependencyInjection; using Umbraco.Web.Common.Security; +using Microsoft.AspNetCore.Routing; namespace Umbraco.Web.BackOffice.Security { @@ -36,6 +37,7 @@ namespace Umbraco.Web.BackOffice.Security private readonly IUserService _userService; private readonly IIpResolver _ipResolver; private readonly BackOfficeSessionIdValidator _sessionIdValidator; + private readonly LinkGenerator _linkGenerator; public ConfigureBackOfficeCookieOptions( IUmbracoContextAccessor umbracoContextAccessor, @@ -47,7 +49,8 @@ namespace Umbraco.Web.BackOffice.Security IRequestCache requestCache, IUserService userService, IIpResolver ipResolver, - BackOfficeSessionIdValidator sessionIdValidator) + BackOfficeSessionIdValidator sessionIdValidator, + LinkGenerator linkGenerator) { _umbracoContextAccessor = umbracoContextAccessor; _securitySettings = securitySettings; @@ -59,6 +62,7 @@ namespace Umbraco.Web.BackOffice.Security _userService = userService; _ipResolver = ipResolver; _sessionIdValidator = sessionIdValidator; + _linkGenerator = linkGenerator; } public void Configure(string name, CookieAuthenticationOptions options) @@ -98,7 +102,8 @@ namespace Umbraco.Web.BackOffice.Security _runtimeState, _hostingEnvironment, _globalSettings, - _requestCache); + _requestCache, + _linkGenerator); // _explicitPaths); TODO: Implement this once we do OAuth somehow diff --git a/src/Umbraco.Web.Common/Extensions/LinkGeneratorExtensions.cs b/src/Umbraco.Web.Common/Extensions/LinkGeneratorExtensions.cs index d3d89525b4..5d51305ea1 100644 --- a/src/Umbraco.Web.Common/Extensions/LinkGeneratorExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/LinkGeneratorExtensions.cs @@ -6,6 +6,7 @@ using Umbraco.Web.Common.Install; using Umbraco.Core.Hosting; using System.Linq.Expressions; using Umbraco.Web.Common.Controllers; +using System.Linq; namespace Umbraco.Extensions { @@ -57,6 +58,23 @@ namespace Umbraco.Extensions return linkGenerator.GetUmbracoApiService(actionName, typeof(T), id); } + public static string GetUmbracoApiService(this LinkGenerator url, Expression> methodSelector) + where T : UmbracoApiControllerBase + { + var method = ExpressionHelper.GetMethodInfo(methodSelector); + var methodParams = ExpressionHelper.GetMethodParams(methodSelector); + if (method == null) + { + throw new MissingMethodException("Could not find the method " + methodSelector + " on type " + typeof(T) + " or the result "); + } + + if (methodParams.Any() == false) + { + return url.GetUmbracoApiService(method.Name); + } + return url.GetUmbracoApiService(method.Name, methodParams.Values.First()); + } + public static string GetUmbracoApiServiceBaseUrl(this LinkGenerator linkGenerator, Expression> methodSelector) where T : UmbracoApiControllerBase { diff --git a/src/Umbraco.Web/Security/AppBuilderExtensions.cs b/src/Umbraco.Web/Security/AppBuilderExtensions.cs index f766a142d9..d95c19bedf 100644 --- a/src/Umbraco.Web/Security/AppBuilderExtensions.cs +++ b/src/Umbraco.Web/Security/AppBuilderExtensions.cs @@ -29,228 +29,6 @@ namespace Umbraco.Web.Security /// public static class AppBuilderExtensions { - /// - /// Configure Default Identity User Manager for Umbraco - /// - public static void ConfigureUserManagerForUmbracoBackOffice(this IAppBuilder app, - ServiceContext services, - IGlobalSettings globalSettings, - UmbracoMapper mapper, - // TODO: This could probably be optional? - IPasswordConfiguration passwordConfiguration, - IIpResolver ipResolver) - { - if (services == null) throw new ArgumentNullException(nameof(services)); - - //Configure Umbraco user manager to be created per request - app.CreatePerOwinContext( - (options, owinContext) => BackOfficeOwinUserManager.Create( - services.UserService, - services.EntityService, - services.ExternalLoginService, - globalSettings, - mapper, - passwordConfiguration, - ipResolver, - new BackOfficeIdentityErrorDescriber(), - app.GetDataProtectionProvider(), - new NullLogger>())); - - app.SetBackOfficeUserManagerType(); - - //Create a sign in manager per request - app.CreatePerOwinContext((options, context) => BackOfficeSignInManager.Create(context, globalSettings, app.CreateLogger())); - } - - /// - /// Configure a custom UserStore with the Identity User Manager for Umbraco - /// - public static void ConfigureUserManagerForUmbracoBackOffice(this IAppBuilder app, - IRuntimeState runtimeState, - IGlobalSettings globalSettings, - BackOfficeUserStore customUserStore, - // TODO: This could probably be optional? - IPasswordConfiguration passwordConfiguration, - IIpResolver ipResolver) - { - if (runtimeState == null) throw new ArgumentNullException(nameof(runtimeState)); - if (customUserStore == null) throw new ArgumentNullException(nameof(customUserStore)); - - //Configure Umbraco user manager to be created per request - app.CreatePerOwinContext( - (options, owinContext) => BackOfficeOwinUserManager.Create( - passwordConfiguration, - ipResolver, - customUserStore, - new BackOfficeIdentityErrorDescriber(), - app.GetDataProtectionProvider(), - new NullLogger>())); - - app.SetBackOfficeUserManagerType(); - - //Create a sign in manager per request - app.CreatePerOwinContext((options, context) => BackOfficeSignInManager.Create(context, globalSettings, app.CreateLogger(typeof(BackOfficeSignInManager).FullName))); - } - - /// - /// Ensures that the UmbracoBackOfficeAuthenticationMiddleware is assigned to the pipeline - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// By default this will be configured to execute on PipelineStage.Authenticate - /// - public static IAppBuilder UseUmbracoBackOfficeCookieAuthentication(this IAppBuilder app, - IUmbracoContextAccessor umbracoContextAccessor, - IRuntimeState runtimeState, - IUserService userService, - IGlobalSettings globalSettings, - ISecuritySettings securitySettings, - IHostingEnvironment hostingEnvironment, - IRequestCache requestCache) - { - return app.UseUmbracoBackOfficeCookieAuthentication(umbracoContextAccessor, runtimeState, userService, globalSettings, securitySettings, hostingEnvironment, requestCache, PipelineStage.Authenticate); - } - - /// - /// Ensures that the UmbracoBackOfficeAuthenticationMiddleware is assigned to the pipeline - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Configurable pipeline stage - /// - /// - public static IAppBuilder UseUmbracoBackOfficeCookieAuthentication(this IAppBuilder app, - IUmbracoContextAccessor umbracoContextAccessor, - IRuntimeState runtimeState, - IUserService userService, - IGlobalSettings globalSettings, - ISecuritySettings securitySettings, - IHostingEnvironment hostingEnvironment, - IRequestCache requestCache, - PipelineStage stage) - { - //Create the default options and provider - var authOptions = app.CreateUmbracoCookieAuthOptions(umbracoContextAccessor, globalSettings, runtimeState, securitySettings, hostingEnvironment, requestCache); - - return app.UseUmbracoBackOfficeCookieAuthentication(umbracoContextAccessor, runtimeState, globalSettings, securitySettings, hostingEnvironment, requestCache, authOptions, stage); - } - - /// - /// Ensures that the UmbracoBackOfficeAuthenticationMiddleware is assigned to the pipeline - /// - /// - /// - /// - /// - /// - /// - /// - /// Custom auth cookie options can be specified to have more control over the cookie authentication logic - /// - /// Configurable pipeline stage - /// - /// - public static IAppBuilder UseUmbracoBackOfficeCookieAuthentication(this IAppBuilder app, IUmbracoContextAccessor umbracoContextAccessor, IRuntimeState runtimeState, IGlobalSettings globalSettings, - ISecuritySettings securitySettings, IHostingEnvironment hostingEnvironment, IRequestCache requestCache, CookieAuthenticationOptions cookieOptions, PipelineStage stage) - { - if (app == null) throw new ArgumentNullException(nameof(app)); - if (runtimeState == null) throw new ArgumentNullException(nameof(runtimeState)); - if (cookieOptions == null) throw new ArgumentNullException(nameof(cookieOptions)); - if (cookieOptions.Provider == null) - throw new ArgumentNullException("cookieOptions.Provider cannot be null.", nameof(cookieOptions)); - if (cookieOptions.Provider is BackOfficeCookieAuthenticationProvider == false) - throw new ArgumentException($"cookieOptions.Provider must be of type {typeof(BackOfficeCookieAuthenticationProvider)}.", nameof(cookieOptions)); - - app.UseUmbracoBackOfficeCookieAuthenticationInternal(cookieOptions, runtimeState, requestCache, stage); - - //don't apply if app is not ready - if (runtimeState.Level != RuntimeLevel.Upgrade && runtimeState.Level != RuntimeLevel.Run) return app; - - var backOfficePath = globalSettings.GetBackOfficePath(hostingEnvironment); - var cookieAuthOptions = app.CreateUmbracoCookieAuthOptions( - umbracoContextAccessor, globalSettings, runtimeState, securitySettings, - //This defines the explicit path read cookies from for this middleware - hostingEnvironment, requestCache, new[] {$"{backOfficePath}/backoffice/UmbracoApi/Authentication/GetRemainingTimeoutSeconds"}); - cookieAuthOptions.Provider = cookieOptions.Provider; - - //This is a custom middleware, we need to return the user's remaining logged in seconds - app.Use( - cookieAuthOptions, - Current.Configs.Global(), - Current.Configs.Security(), - app.CreateLogger(), - Current.HostingEnvironment); - - //This is required so that we can read the auth ticket format outside of this pipeline - app.CreatePerOwinContext( - (options, context) => new UmbracoAuthTicketDataProtector(cookieOptions.TicketDataFormat)); - - return app; - } - - private static bool _markerSet = false; - - /// - /// This registers the exact type of the user manager in owin so we can extract it - /// when required in order to extract the user manager instance - /// - /// - /// - /// - /// - /// This is required because a developer can specify a custom user manager and due to generic types the key name will registered - /// differently in the owin context - /// - private static void SetBackOfficeUserManagerType(this IAppBuilder app) - where TManager : BackOfficeUserManager - where TUser : BackOfficeIdentityUser - { - if (_markerSet) throw new InvalidOperationException("The back office user manager marker has already been set, only one back office user manager can be configured"); - - //on each request set the user manager getter - - // this is required purely because Microsoft.Owin.IOwinContext is super inflexible with it's Get since it can only be - // a generic strongly typed instance - app.Use((context, func) => - { - context.Set(BackOfficeOwinUserManager.OwinMarkerKey, new BackOfficeUserManagerMarker()); - return func(); - }); - } - - private static void UseUmbracoBackOfficeCookieAuthenticationInternal(this IAppBuilder app, CookieAuthenticationOptions options, IRuntimeState runtimeState, IRequestCache requestCache, PipelineStage stage) - { - if (app == null) throw new ArgumentNullException(nameof(app)); - if (runtimeState == null) throw new ArgumentNullException(nameof(runtimeState)); - - //First the normal cookie middleware - app.Use(typeof(CookieAuthenticationMiddleware), app, options); - //don't apply if app is not ready - if (runtimeState.Level == RuntimeLevel.Upgrade || runtimeState.Level == RuntimeLevel.Run) - { - //Then our custom middlewares - app.Use(typeof(ForceRenewalCookieAuthenticationMiddleware), app, options, Current.UmbracoContextAccessor, requestCache); - app.Use(typeof(FixWindowsAuthMiddlware)); - } - - //Marks all of the above middlewares to execute on Authenticate - app.UseStageMarker(stage); - } - /// /// Ensures that the cookie middleware for validating external logins is assigned to the pipeline with the correct @@ -297,8 +75,6 @@ namespace Umbraco.Web.Security AuthenticationMode = AuthenticationMode.Passive, CookieName = Constants.Security.BackOfficeExternalCookieName, ExpireTimeSpan = TimeSpan.FromMinutes(5), - //Custom cookie manager so we can filter requests - CookieManager = new BackOfficeCookieManager(umbracoContextAccessor, runtimeState, hostingEnvironment, globalSettings, requestCache), CookiePath = "/", CookieSecure = globalSettings.UseHttps ? CookieSecureOption.Always : CookieSecureOption.SameAsRequest, CookieHttpOnly = true, @@ -353,8 +129,8 @@ namespace Umbraco.Web.Security { if (runtimeState.Level != RuntimeLevel.Run) return app; - var authOptions = app.CreateUmbracoCookieAuthOptions(umbracoContextAccessor, globalSettings, runtimeState, securitySettings, hostingEnvironment, requestCache); - app.Use(typeof(PreviewAuthenticationMiddleware), authOptions, globalSettings, hostingEnvironment); + //var authOptions = app.CreateUmbracoCookieAuthOptions(umbracoContextAccessor, globalSettings, runtimeState, securitySettings, hostingEnvironment, requestCache); + app.Use(typeof(PreviewAuthenticationMiddleware), /*authOptions*/null, globalSettings, hostingEnvironment); // This middleware must execute at least on PostAuthentication, by default it is on Authorize // The middleware needs to execute after the RoleManagerModule executes which is during PostAuthenticate, @@ -372,40 +148,6 @@ namespace Umbraco.Web.Security Thread.CurrentThread.SanitizeThreadCulture(); } - /// - /// Create the default umb cookie auth options - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static UmbracoBackOfficeCookieAuthOptions CreateUmbracoCookieAuthOptions(this IAppBuilder app, - IUmbracoContextAccessor umbracoContextAccessor, - IGlobalSettings globalSettings, IRuntimeState runtimeState, ISecuritySettings securitySettings, IHostingEnvironment hostingEnvironment, IRequestCache requestCache, string[] explicitPaths = null) - { - //this is how aspnet wires up the default AuthenticationTicket protector so we'll use the same code - var ticketDataFormat = new TicketDataFormat( - app.CreateDataProtector(typeof (CookieAuthenticationMiddleware).FullName, - Constants.Security.BackOfficeAuthenticationType, - "v1")); - - var authOptions = new UmbracoBackOfficeCookieAuthOptions( - explicitPaths, - umbracoContextAccessor, - securitySettings, - globalSettings, - hostingEnvironment, - runtimeState, - ticketDataFormat, - requestCache); - - return authOptions; - } public static IAppBuilder CreatePerOwinContext(this IAppBuilder app, Func createCallback) where T : class, IDisposable { diff --git a/src/Umbraco.Web/Security/BackOfficeCookieManager.cs b/src/Umbraco.Web/Security/BackOfficeCookieManager.cs deleted file mode 100644 index 73c143afd4..0000000000 --- a/src/Umbraco.Web/Security/BackOfficeCookieManager.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using Microsoft.Owin; -using Microsoft.Owin.Infrastructure; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; -using Umbraco.Core.Hosting; -using Umbraco.Core.IO; -using Umbraco.Core.Security; - -namespace Umbraco.Web.Security -{ - /// - /// A custom cookie manager that is used to read the cookie from the request. - /// - /// - /// Umbraco's back office cookie needs to be read on two paths: /umbraco and /install and /base therefore we cannot just set the cookie path to be /umbraco, - /// instead we'll specify our own cookie manager and return null if the request isn't for an acceptable path. - /// - internal class BackOfficeCookieManager : ChunkingCookieManager, Microsoft.Owin.Infrastructure.ICookieManager - { - private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly IRuntimeState _runtime; - private readonly IHostingEnvironment _hostingEnvironment; - private readonly IGlobalSettings _globalSettings; - private readonly IRequestCache _requestCache; - private readonly string[] _explicitPaths; - private readonly string _getRemainingSecondsPath; - - public BackOfficeCookieManager(IUmbracoContextAccessor umbracoContextAccessor, IRuntimeState runtime, IHostingEnvironment hostingEnvironment, IGlobalSettings globalSettings, IRequestCache requestCache) - : this(umbracoContextAccessor, runtime, hostingEnvironment, globalSettings, requestCache, null) - { } - - public BackOfficeCookieManager(IUmbracoContextAccessor umbracoContextAccessor, IRuntimeState runtime, IHostingEnvironment hostingEnvironment, IGlobalSettings globalSettings, IRequestCache requestCache, IEnumerable explicitPaths) - { - _umbracoContextAccessor = umbracoContextAccessor; - _runtime = runtime; - _hostingEnvironment = hostingEnvironment; - _globalSettings = globalSettings; - _requestCache = requestCache; - _explicitPaths = explicitPaths?.ToArray(); - var backOfficePath = _globalSettings.GetBackOfficePath(_hostingEnvironment); - _getRemainingSecondsPath = $"{backOfficePath}/backoffice/UmbracoApi/Authentication/GetRemainingTimeoutSeconds"; - } - - /// - /// Explicitly implement this so that we filter the request - /// - /// - /// - /// - string Microsoft.Owin.Infrastructure.ICookieManager.GetRequestCookie(IOwinContext context, string key) - { - if (_umbracoContextAccessor.UmbracoContext == null || context.Request.Uri.IsClientSideRequest()) - { - return null; - } - - return ShouldAuthenticateRequest( - context, - _umbracoContextAccessor.UmbracoContext.OriginalRequestUrl) == false - //Don't auth request, don't return a cookie - ? null - //Return the default implementation - : GetRequestCookie(context, key); - } - - /// - /// Determines if we should authenticate the request - /// - /// - /// - /// - /// - /// - /// We auth the request when: - /// * it is a back office request - /// * it is an installer request - /// * it is a /base request - /// * it is a preview request - /// - internal bool ShouldAuthenticateRequest(IOwinContext owinContext, Uri originalRequestUrl, bool checkForceAuthTokens = true) - { - // Do not authenticate the request if we are not running (don't have a db, are not configured) - since we will never need - // to know a current user in this scenario - we treat it as a new install. Without this we can have some issues - // when people have older invalid cookies on the same domain since our user managers might attempt to lookup a user - // and we don't even have a db. - // was: app.IsConfigured == false (equiv to !Run) && dbContext.IsDbConfigured == false (equiv to Install) - // so, we handle .Install here and NOT .Upgrade - if (_runtime.Level == RuntimeLevel.Install) - return false; - - var request = owinContext.Request; - //check the explicit paths - if (_explicitPaths != null) - { - return _explicitPaths.Any(x => x.InvariantEquals(request.Uri.AbsolutePath)); - } - - //check user seconds path - if (request.Uri.AbsolutePath.InvariantEquals(_getRemainingSecondsPath)) return false; - - if (//check the explicit flag - (checkForceAuthTokens && owinContext.Get(Constants.Security.ForceReAuthFlag) != null) - || (checkForceAuthTokens && _requestCache.IsAvailable && _requestCache.Get(Constants.Security.ForceReAuthFlag) != null) - //check back office - || request.Uri.IsBackOfficeRequest(_globalSettings, _hostingEnvironment) - //check installer - || request.Uri.IsInstallerRequest(_hostingEnvironment)) - { - return true; - } - return false; - } - - } -} diff --git a/src/Umbraco.Web/Security/ForceRenewalCookieAuthenticationHandler.cs b/src/Umbraco.Web/Security/ForceRenewalCookieAuthenticationHandler.cs index b3092b90c9..deb8e9fd63 100644 --- a/src/Umbraco.Web/Security/ForceRenewalCookieAuthenticationHandler.cs +++ b/src/Umbraco.Web/Security/ForceRenewalCookieAuthenticationHandler.cs @@ -57,19 +57,19 @@ namespace Umbraco.Web.Security protected override Task ApplyResponseGrantAsync() { if (_umbracoContextAccessor.UmbracoContext == null || Context.Request.Uri.IsClientSideRequest()) - { + { return Task.FromResult(0); } //Now we need to check if we should force renew this based on a flag in the context and whether this is a request that is not normally renewed by OWIN... // which means that it is not a normal URL that is authenticated. - var normalAuthUrl = ((BackOfficeCookieManager) Options.CookieManager) - .ShouldAuthenticateRequest(Context, _umbracoContextAccessor.UmbracoContext.OriginalRequestUrl, - //Pass in false, we want to know if this is a normal auth'd page - checkForceAuthTokens: false); - //This is auth'd normally, so OWIN will naturally take care of the cookie renewal - if (normalAuthUrl) return Task.FromResult(0); + //var normalAuthUrl = ((BackOfficeCookieManager) Options.CookieManager) + // .ShouldAuthenticateRequest(Context, _umbracoContextAccessor.UmbracoContext.OriginalRequestUrl, + // //Pass in false, we want to know if this is a normal auth'd page + // checkForceAuthTokens: false); + ////This is auth'd normally, so OWIN will naturally take care of the cookie renewal + //if (normalAuthUrl) return Task.FromResult(0); //check for the special flag in either the owin or http context var shouldRenew = Context.Get(Constants.Security.ForceReAuthFlag) != null || (_requestCache.IsAvailable && _requestCache.Get(Constants.Security.ForceReAuthFlag) != null); diff --git a/src/Umbraco.Web/Security/UmbracoBackOfficeCookieAuthOptions.cs b/src/Umbraco.Web/Security/UmbracoBackOfficeCookieAuthOptions.cs index 68155f47b7..34669bc5ae 100644 --- a/src/Umbraco.Web/Security/UmbracoBackOfficeCookieAuthOptions.cs +++ b/src/Umbraco.Web/Security/UmbracoBackOfficeCookieAuthOptions.cs @@ -18,34 +18,6 @@ namespace Umbraco.Web.Security { public int LoginTimeoutMinutes { get; } - public UmbracoBackOfficeCookieAuthOptions( - string[] explicitPaths, - IUmbracoContextAccessor umbracoContextAccessor, - ISecuritySettings securitySettings, - IGlobalSettings globalSettings, - IHostingEnvironment hostingEnvironment, - IRuntimeState runtimeState, - ISecureDataFormat secureDataFormat, - IRequestCache requestCache) - { - var secureDataFormat1 = secureDataFormat ?? throw new ArgumentNullException(nameof(secureDataFormat)); - LoginTimeoutMinutes = globalSettings.TimeOutInMinutes; - AuthenticationType = Constants.Security.BackOfficeAuthenticationType; - - SlidingExpiration = true; - ExpireTimeSpan = TimeSpan.FromMinutes(LoginTimeoutMinutes); - CookieDomain = securitySettings.AuthCookieDomain; - CookieName = securitySettings.AuthCookieName; - CookieHttpOnly = true; - CookieSecure = globalSettings.UseHttps ? CookieSecureOption.Always : CookieSecureOption.SameAsRequest; - CookiePath = "/"; - - TicketDataFormat = new UmbracoSecureDataFormat(LoginTimeoutMinutes, secureDataFormat1); - - //Custom cookie manager so we can filter requests - CookieManager = new BackOfficeCookieManager(umbracoContextAccessor, runtimeState, hostingEnvironment, globalSettings, requestCache, explicitPaths); - } - /// /// Creates the cookie options for saving the auth cookie /// diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 51249d6348..3efbf002c2 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -339,7 +339,6 @@ - diff --git a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs index 22d7bd77d7..f800707476 100644 --- a/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs +++ b/src/Umbraco.Web/UmbracoDefaultOwinStartup.cs @@ -63,7 +63,6 @@ namespace Umbraco.Web protected virtual void ConfigureServices(IAppBuilder app, ServiceContext services) { app.SetUmbracoLoggerFactory(); - ConfigureUmbracoUserManager(app); } /// @@ -81,21 +80,6 @@ namespace Umbraco.Web .FinalizeMiddlewareConfiguration(); } - /// - /// Configure the Identity user manager for use with Umbraco Back office - /// - /// - protected virtual void ConfigureUmbracoUserManager(IAppBuilder app) - { - // (EXPERT: an overload accepts a custom BackOfficeUserStore implementation) - app.ConfigureUserManagerForUmbracoBackOffice( - Services, - GlobalSettings, - Mapper, - UserPasswordConfig, - IpResolver); - } - /// /// Configure external/OAuth login providers /// @@ -105,7 +89,8 @@ namespace Umbraco.Web // Ensure owin is configured for Umbraco back office authentication. // Front-end OWIN cookie configuration must be declared after this code. app - .UseUmbracoBackOfficeCookieAuthentication(UmbracoContextAccessor, RuntimeState, Services.UserService, GlobalSettings, SecuritySettings, HostingEnvironment, RequestCache, PipelineStage.Authenticate) + // already moved to netcore + //.UseUmbracoBackOfficeCookieAuthentication(UmbracoContextAccessor, RuntimeState, Services.UserService, GlobalSettings, SecuritySettings, HostingEnvironment, RequestCache, PipelineStage.Authenticate) .UseUmbracoBackOfficeExternalCookieAuthentication(UmbracoContextAccessor, RuntimeState, GlobalSettings, HostingEnvironment, RequestCache, PipelineStage.Authenticate) .UseUmbracoPreviewAuthentication(UmbracoContextAccessor, RuntimeState, GlobalSettings, SecuritySettings, HostingEnvironment, RequestCache, PipelineStage.Authorize); }