diff --git a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js index 85578dbb99..8d99086ba5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js @@ -56,7 +56,7 @@ angular.module('umbraco.services') /** Method to count down the current user's timeout seconds, - this will continually count down their current remaining seconds every 2 seconds until + this will continually count down their current remaining seconds every 5 seconds until there are no more seconds remaining. */ function countdownUserTimeout() { @@ -64,8 +64,8 @@ angular.module('umbraco.services') $timeout(function () { if (currentUser) { - //countdown by 2 seconds since that is how long our timer is for. - currentUser.remainingAuthSeconds -= 2; + //countdown by 5 seconds since that is how long our timer is for. + currentUser.remainingAuthSeconds -= 5; //if there are more than 30 remaining seconds, recurse! if (currentUser.remainingAuthSeconds > 30) { @@ -128,7 +128,7 @@ angular.module('umbraco.services') } } } - }, 2000, //every 2 seconds + }, 5000, //every 5 seconds false); //false = do NOT execute a digest for every iteration } diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index f51d61b15c..c2de5441ab 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -151,7 +151,29 @@ False ..\packages\Lucene.Net.2.9.4.1\lib\net40\Lucene.Net.dll + + ..\packages\Microsoft.AspNet.Identity.Core.2.1.0\lib\net45\Microsoft.AspNet.Identity.Core.dll + + + ..\packages\Microsoft.AspNet.Identity.Owin.2.1.0\lib\net45\Microsoft.AspNet.Identity.Owin.dll + + + False + ..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll + + + ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll + + + ..\packages\Microsoft.Owin.Security.2.1.0\lib\net45\Microsoft.Owin.Security.dll + + + ..\packages\Microsoft.Owin.Security.Cookies.2.1.0\lib\net45\Microsoft.Owin.Security.Cookies.dll + + + ..\packages\Microsoft.Owin.Security.OAuth.2.1.0\lib\net45\Microsoft.Owin.Security.OAuth.dll + ..\packages\Microsoft.Bcl.Async.1.0.165\lib\net45\Microsoft.Threading.Tasks.dll @@ -178,6 +200,9 @@ False ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll + + ..\packages\Owin.1.0\lib\net40\Owin.dll + System @@ -324,6 +349,7 @@ Properties\SolutionInfo.cs + loadStarterKits.ascx ASPXCodeBehind @@ -2518,7 +2544,6 @@ - diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index da11256606..150759f582 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -9,6 +9,8 @@ + + @@ -21,10 +23,16 @@ + + + + + + diff --git a/src/Umbraco.Web/Security/Identity/AppBuilderExtensions.cs b/src/Umbraco.Web/Security/Identity/AppBuilderExtensions.cs new file mode 100644 index 0000000000..f2ef0a010d --- /dev/null +++ b/src/Umbraco.Web/Security/Identity/AppBuilderExtensions.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Web; +using Microsoft.AspNet.Identity.Owin; +using Microsoft.Owin; +using Microsoft.Owin.Extensions; +using Owin; +using Umbraco.Core; +using Umbraco.Core.Configuration; + +namespace Umbraco.Web.Security.Identity +{ + public static class AppBuilderExtensions + { + ///// + ///// Configure Identity User Manager for Umbraco + ///// + ///// + ///// + ///// + //public static void ConfigureUserManagerForUmbraco(this IAppBuilder app, ApplicationContext appContext) + // where T : UmbracoIdentityUser, new() + //{ + + // //Don't proceed if the app is not ready + // if (appContext.IsConfigured == false + // || appContext.DatabaseContext == null + // || appContext.DatabaseContext.IsDatabaseConfigured == false) return; + + // //Configure Umbraco user manager to be created per request + // app.CreatePerOwinContext>( + // (o, c) => UmbracoMembersUserManager.Create( + // o, c, ApplicationContext.Current.Services.MemberService)); + + // //Configure Umbraco member event handler to be created per request - this will ensure that the + // // external logins are kept in sync if members are deleted from Umbraco + // app.CreatePerOwinContext>((options, context) => new MembersEventHandler(context)); + + // //TODO: This is just for the mem leak fix + // app.CreatePerOwinContext, UmbracoMembersUserManager>>( + // (o, c) => new OwinContextDisposal, UmbracoMembersUserManager>(c)); + //} + + /// + /// Ensures that the UmbracoBackOfficeAuthenticationMiddleware is assigned to the pipeline + /// + /// + /// + public static IAppBuilder UseUmbracoBackAuthentication(this IAppBuilder app) + { + if (app == null) throw new ArgumentNullException("app"); + + app.Use(typeof (UmbracoBackOfficeAuthenticationMiddleware), + //ctor params + app, new UmbracoBackOfficeAuthenticationOptions(), UmbracoConfig.For.UmbracoSettings().Security); + + app.UseStageMarker(PipelineStage.Authenticate); + return app; + } + + //This is a fix for OWIN mem leak! + //http://stackoverflow.com/questions/24378856/memory-leak-in-owin-appbuilderextensions/24819543#24819543 + private class OwinContextDisposal : IDisposable + where T1 : IDisposable + where T2 : IDisposable + { + private readonly List _disposables = new List(); + private bool _disposed = false; + + public OwinContextDisposal(IOwinContext owinContext) + { + if (HttpContext.Current == null) return; + + _disposables.Add(owinContext.Get()); + _disposables.Add(owinContext.Get()); + + HttpContext.Current.DisposeOnPipelineCompleted(this); + } + + public void Dispose() + { + if (_disposed) return; + foreach (var disposable in _disposables) + { + disposable.Dispose(); + } + _disposed = true; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Security/Identity/OwinExtensions.cs b/src/Umbraco.Web/Security/Identity/OwinExtensions.cs new file mode 100644 index 0000000000..4b83f97bd3 --- /dev/null +++ b/src/Umbraco.Web/Security/Identity/OwinExtensions.cs @@ -0,0 +1,19 @@ +using System.Web; +using Microsoft.Owin; + +namespace Umbraco.Web.Security.Identity +{ + internal static class OwinExtensions + { + /// + /// Nasty little hack to get httpcontextbase from an owin context + /// + /// + /// + public static HttpContextBase HttpContextFromOwinContext(this IOwinContext owinContext) + { + return owinContext.Get(typeof(HttpContextBase).FullName); + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationHandler.cs b/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationHandler.cs new file mode 100644 index 0000000000..1b0adc604c --- /dev/null +++ b/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationHandler.cs @@ -0,0 +1,87 @@ +using System; +using System.Reflection; +using System.Threading.Tasks; +using System.Web.Security; +using Microsoft.Owin; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Infrastructure; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Security; +using Umbraco.Core; + +namespace Umbraco.Web.Security.Identity +{ + /// + /// Used to allow normal Umbraco back office authentication to work + /// + public class UmbracoBackOfficeAuthenticationHandler : AuthenticationHandler + { + private readonly ISecuritySection _securitySection; + + public UmbracoBackOfficeAuthenticationHandler(ISecuritySection securitySection) + { + _securitySection = securitySection; + } + + /// + /// Checks if we should authentication the request (i.e. is back office) and if so gets the forms auth ticket in the request + /// and returns an AuthenticationTicket based on that. + /// + /// + /// + /// It's worth noting that the UmbracoModule still executes and performs the authentication, however this also needs to execute + /// so that it assigns the new Principal object on the OWIN request: + /// http://brockallen.com/2013/10/27/host-authentication-and-web-api-with-owin-and-active-vs-passive-authentication-middleware/ + /// + protected override Task AuthenticateCoreAsync() + { + if (ShouldAuthRequest()) + { + var authTicket = GetAuthTicket(Request, _securitySection.AuthCookieName); + if (authTicket != null) + { + return Task.FromResult(new AuthenticationTicket(new UmbracoBackOfficeIdentity(authTicket), new AuthenticationProperties())); + } + } + + return Task.FromResult(null); + } + + private bool ShouldAuthRequest() + { + var httpContext = Context.HttpContextFromOwinContext(); + + // do not process if client-side request + if (httpContext.Request.Url.IsClientSideRequest()) + return false; + + return UmbracoModule.ShouldAuthenticateRequest(httpContext.Request, Request.Uri); + } + + /// + /// Returns the current FormsAuth ticket in the request + /// + /// + /// + /// + private static FormsAuthenticationTicket GetAuthTicket(IOwinRequest request, string cookieName) + { + if (request == null) throw new ArgumentNullException("request"); + + var formsCookie = request.Cookies[cookieName]; + if (formsCookie == null) + { + return null; + } + //get the ticket + try + { + return FormsAuthentication.Decrypt(formsCookie); + } + catch (Exception) + { + return null; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationMiddleware.cs b/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationMiddleware.cs new file mode 100644 index 0000000000..1275b72c08 --- /dev/null +++ b/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationMiddleware.cs @@ -0,0 +1,26 @@ +using Microsoft.Owin; +using Microsoft.Owin.Security.Infrastructure; +using Owin; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Web.Security.Identity +{ + /// + /// Used to enable the normal Umbraco back office authentication to operate + /// + public class UmbracoBackOfficeAuthenticationMiddleware : AuthenticationMiddleware + { + private readonly ISecuritySection _securitySection; + + public UmbracoBackOfficeAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, UmbracoBackOfficeAuthenticationOptions options, ISecuritySection securitySection) + : base(next, options) + { + _securitySection = securitySection; + } + + protected override AuthenticationHandler CreateHandler() + { + return new UmbracoBackOfficeAuthenticationHandler(_securitySection); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationOptions.cs b/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationOptions.cs new file mode 100644 index 0000000000..c7609a8e83 --- /dev/null +++ b/src/Umbraco.Web/Security/Identity/UmbracoBackOfficeAuthenticationOptions.cs @@ -0,0 +1,18 @@ +using Microsoft.Owin.Security; + +namespace Umbraco.Web.Security.Identity +{ + /// + /// Umbraco auth options - really just ensures that it is operating in Active mode + /// + public sealed class UmbracoBackOfficeAuthenticationOptions : AuthenticationOptions + { + public UmbracoBackOfficeAuthenticationOptions() + : base("UmbracoBackOffice") + { + //Must be active, this needs to look at each request to determine if it should execute, + // if set to passive this will not be the case + AuthenticationMode = AuthenticationMode.Active; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e261c048d3..45e0ce5fe2 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -131,7 +131,25 @@ False ..\packages\Lucene.Net.2.9.4.1\lib\net40\Lucene.Net.dll + + ..\packages\Microsoft.AspNet.Identity.Core.2.1.0\lib\net45\Microsoft.AspNet.Identity.Core.dll + + + ..\packages\Microsoft.AspNet.Identity.Owin.2.1.0\lib\net45\Microsoft.AspNet.Identity.Owin.dll + + + ..\packages\Microsoft.Owin.2.1.0\lib\net45\Microsoft.Owin.dll + + + ..\packages\Microsoft.Owin.Security.2.1.0\lib\net45\Microsoft.Owin.Security.dll + + + ..\packages\Microsoft.Owin.Security.Cookies.2.1.0\lib\net45\Microsoft.Owin.Security.Cookies.dll + + + ..\packages\Microsoft.Owin.Security.OAuth.2.1.0\lib\net45\Microsoft.Owin.Security.OAuth.dll + True ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll @@ -148,6 +166,9 @@ False ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll + + ..\packages\Owin.1.0\lib\net40\Owin.dll + System @@ -518,6 +539,11 @@ + + + + + diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index 294079e830..3fade0702d 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -6,6 +6,8 @@ + + @@ -17,9 +19,14 @@ + + + + +