From dcf730a1bc968ba7245a8d83fed04ec9d76c0a03 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 31 Jul 2013 18:21:27 +1000 Subject: [PATCH] ensures the ui cultures are set in the back office. adds WebSecurity as a dependency for UmbracoContext. --- .../Security/UmbracoBackOfficeIdentity.cs | 11 ++- src/Umbraco.Core/Security/UserData.cs | 5 +- .../PublishedContentCacheTests.cs | 4 +- .../TestHelpers/BaseDatabaseFactoryTest.cs | 7 +- src/Umbraco.Web/Security/WebSecurity.cs | 29 ++++--- .../Standalone/StandaloneBootManager.cs | 6 +- src/Umbraco.Web/UmbracoContext.cs | 75 +++++++++++++++++-- src/Umbraco.Web/UmbracoModule.cs | 11 ++- .../WebApi/Binders/ContentItemBaseBinder.cs | 7 +- .../WebApi/UmbracoApiController.cs | 5 ++ src/Umbraco.Web/WebBootManager.cs | 7 +- .../BasePages/BasePage.cs | 2 +- src/umbraco.businesslogic/ui.cs | 7 +- 13 files changed, 148 insertions(+), 28 deletions(-) diff --git a/src/Umbraco.Core/Security/UmbracoBackOfficeIdentity.cs b/src/Umbraco.Core/Security/UmbracoBackOfficeIdentity.cs index 3223e37faf..67cff98ac4 100644 --- a/src/Umbraco.Core/Security/UmbracoBackOfficeIdentity.cs +++ b/src/Umbraco.Core/Security/UmbracoBackOfficeIdentity.cs @@ -47,7 +47,7 @@ namespace Umbraco.Core.Security } } - public int Id + public object Id { get { @@ -65,6 +65,15 @@ namespace Umbraco.Core.Security } } + public string Culture + { + get + { + EnsureDeserialized(); + return DeserializedData.Culture; + } + } + //public int SessionTimeout //{ // get diff --git a/src/Umbraco.Core/Security/UserData.cs b/src/Umbraco.Core/Security/UserData.cs index 61d337d813..163bc91108 100644 --- a/src/Umbraco.Core/Security/UserData.cs +++ b/src/Umbraco.Core/Security/UserData.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Security } [DataMember(Name = "id")] - public int Id { get; set; } + public object Id { get; set; } [DataMember(Name = "roles")] public string[] Roles { get; set; } @@ -36,5 +36,8 @@ namespace Umbraco.Core.Security [DataMember(Name = "allowedApps")] public string[] AllowedApplications { get; set; } + + [DataMember(Name = "culture")] + public string Culture { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs index d1abe13d54..b0b03c5a78 100644 --- a/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs @@ -10,6 +10,7 @@ using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; +using Umbraco.Web.Security; using umbraco.BusinessLogic; namespace Umbraco.Tests.PublishedCache @@ -92,7 +93,8 @@ namespace Umbraco.Tests.PublishedCache _umbracoContext = new UmbracoContext( _httpContextFactory.HttpContext, ApplicationContext.Current, - new PublishedCaches(cache, new PublishedMediaCache())); + new PublishedCaches(cache, new PublishedMediaCache()), + new WebSecurity(_httpContextFactory.HttpContext, ApplicationContext.Current)); _cache = _umbracoContext.ContentCache; } diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 6e55342833..7db1a236f6 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -24,6 +24,7 @@ using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; +using Umbraco.Web.Security; using umbraco.BusinessLogic; namespace Umbraco.Tests.TestHelpers @@ -284,10 +285,12 @@ namespace Umbraco.Tests.TestHelpers PublishedContentCache.UnitTesting = true; + var httpContext = GetHttpContextFactory(url, routeData).HttpContext; var ctx = new UmbracoContext( - GetHttpContextFactory(url, routeData).HttpContext, + httpContext, ApplicationContext, - new PublishedCaches(cache, new PublishedMediaCache())); + new PublishedCaches(cache, new PublishedMediaCache()), + new WebSecurity(httpContext, ApplicationContext)); if (setSingleton) { diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index ab7286dec1..3cc853dfe0 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -6,13 +6,16 @@ using System.Web.Security; using Newtonsoft.Json.Linq; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; using Umbraco.Core.Logging; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Security; -using umbraco.BusinessLogic; +using umbraco; using umbraco.DataLayer; using umbraco.businesslogic.Exceptions; -using umbraco.cms.businesslogic.member; +using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings; +using Member = umbraco.cms.businesslogic.member.Member; +using UmbracoSettings = Umbraco.Core.Configuration.UmbracoSettings; +using User = umbraco.BusinessLogic.User; namespace Umbraco.Web.Security { @@ -22,10 +25,12 @@ namespace Umbraco.Web.Security public class WebSecurity : DisposableObject { private HttpContextBase _httpContext; + private ApplicationContext _applicationContext; - public WebSecurity(HttpContextBase httpContext) + public WebSecurity(HttpContextBase httpContext, ApplicationContext applicationContext) { _httpContext = httpContext; + _applicationContext = applicationContext; //This ensures the dispose method is called when the request terminates, though // we also ensure this happens in the Umbraco module because the UmbracoContext is added to the // http context items. @@ -133,24 +138,25 @@ namespace Umbraco.Web.Security /// The user Id public void PerformLogin(int userId) { - var user = User.GetUser(userId); + var user = _applicationContext.Services.UserService.GetUserById(userId); PerformLogin(user); } - internal void PerformLogin(User user) + internal void PerformLogin(IUser user) { _httpContext.CreateUmbracoAuthTicket(new UserData { Id = user.Id, - AllowedApplications = user.GetApplications().Select(x => x.alias).ToArray(), + AllowedApplications = user.AllowedSections.ToArray(), RealName = user.Name, //currently we only have one user type! Roles = new[] { user.UserType.Alias }, - StartContentNode = user.StartNodeId, + StartContentNode = user.StartContentId, StartMediaNode = user.StartMediaId, - Username = user.LoginName + Username = user.Username, + Culture = ui.Culture(user.Language) }); - + LogHelper.Info("User Id: {0} logged in", () => user.Id); } @@ -244,7 +250,7 @@ namespace Umbraco.Web.Security var identity = _httpContext.GetCurrentIdentity(); if (identity == null) return -1; - return identity.Id; + return (int)identity.Id; } /// @@ -364,6 +370,7 @@ namespace Umbraco.Web.Security protected override void DisposeResources() { _httpContext = null; + _applicationContext = null; } } } diff --git a/src/Umbraco.Web/Standalone/StandaloneBootManager.cs b/src/Umbraco.Web/Standalone/StandaloneBootManager.cs index cf2e8f7c11..a6ca751142 100644 --- a/src/Umbraco.Web/Standalone/StandaloneBootManager.cs +++ b/src/Umbraco.Web/Standalone/StandaloneBootManager.cs @@ -6,6 +6,7 @@ using Umbraco.Core; using Umbraco.Core.ObjectResolution; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; +using Umbraco.Web.Security; using umbraco.interfaces; namespace Umbraco.Web.Standalone @@ -79,7 +80,10 @@ namespace Umbraco.Web.Standalone base.FreezeResolution(); var httpContext = new StandaloneHttpContext(); - UmbracoContext.EnsureContext(httpContext, ApplicationContext.Current); + UmbracoContext.EnsureContext( + httpContext, + ApplicationContext.Current, + new WebSecurity(httpContext, ApplicationContext.Current)); } } } diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index e3756ae26e..6135082c81 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -46,6 +46,7 @@ namespace Umbraco.Web /// /// /// + /// /// /// The Singleton context object /// @@ -55,9 +56,34 @@ namespace Umbraco.Web /// during the startup process as well. /// See: http://issues.umbraco.org/issue/U4-1890, http://issues.umbraco.org/issue/U4-1717 /// - public static UmbracoContext EnsureContext(HttpContextBase httpContext, ApplicationContext applicationContext) + public static UmbracoContext EnsureContext( + HttpContextBase httpContext, + ApplicationContext applicationContext, + WebSecurity webSecurity) { - return EnsureContext(httpContext, applicationContext, false); + return EnsureContext(httpContext, applicationContext, webSecurity, false); + } + + /// + /// This is a helper method which is called to ensure that the singleton context is created and the nice url and routing + /// context is created and assigned. + /// + /// + /// + /// + /// The Singleton context object + /// + /// + /// This is created in order to standardize the creation of the singleton. Normally it is created during a request + /// in the UmbracoModule, however this module does not execute during application startup so we need to ensure it + /// during the startup process as well. + /// See: http://issues.umbraco.org/issue/U4-1890, http://issues.umbraco.org/issue/U4-1717 + /// + public static UmbracoContext EnsureContext( + HttpContextBase httpContext, + ApplicationContext applicationContext) + { + return EnsureContext(httpContext, applicationContext, new WebSecurity(httpContext, applicationContext), false); } /// @@ -80,7 +106,40 @@ namespace Umbraco.Web /// during the startup process as well. /// See: http://issues.umbraco.org/issue/U4-1890, http://issues.umbraco.org/issue/U4-1717 /// - public static UmbracoContext EnsureContext(HttpContextBase httpContext, ApplicationContext applicationContext, bool replaceContext) + public static UmbracoContext EnsureContext( + HttpContextBase httpContext, + ApplicationContext applicationContext, + bool replaceContext) + { + return EnsureContext(httpContext, applicationContext, new WebSecurity(httpContext, applicationContext), replaceContext); + } + + /// + /// This is a helper method which is called to ensure that the singleton context is created and the nice url and routing + /// context is created and assigned. + /// + /// + /// + /// + /// + /// if set to true will replace the current singleton with a new one, this is generally only ever used because + /// during application startup the base url domain will not be available so after app startup we'll replace the current + /// context with a new one in which we can access the httpcontext.Request object. + /// + /// + /// The Singleton context object + /// + /// + /// This is created in order to standardize the creation of the singleton. Normally it is created during a request + /// in the UmbracoModule, however this module does not execute during application startup so we need to ensure it + /// during the startup process as well. + /// See: http://issues.umbraco.org/issue/U4-1890, http://issues.umbraco.org/issue/U4-1717 + /// + public static UmbracoContext EnsureContext( + HttpContextBase httpContext, + ApplicationContext applicationContext, + WebSecurity webSecurity, + bool replaceContext) { if (UmbracoContext.Current != null) { @@ -92,7 +151,8 @@ namespace Umbraco.Web var umbracoContext = new UmbracoContext( httpContext, applicationContext, - PublishedCachesResolver.Current.Caches); + PublishedCachesResolver.Current.Caches, + webSecurity); // create the nice urls provider // there's one per request because there are some behavior parameters that can be changed @@ -121,11 +181,13 @@ namespace Umbraco.Web /// /// /// The published caches. + /// /// An optional value overriding detection of preview mode. internal UmbracoContext( HttpContextBase httpContext, ApplicationContext applicationContext, IPublishedCaches publishedCaches, + WebSecurity webSecurity, bool? preview = null) { //This ensures the dispose method is called when the request terminates, though @@ -141,7 +203,7 @@ namespace Umbraco.Web HttpContext = httpContext; Application = applicationContext; - Security = new WebSecurity(HttpContext); + Security = webSecurity; ContentCache = publishedCaches.CreateContextualContentCache(this); MediaCache = publishedCaches.CreateContextualMediaCache(this); @@ -374,10 +436,11 @@ namespace Umbraco.Web protected override void DisposeResources() { - Security.Dispose(); + Security.DisposeIfDisposable(); Security = null; _previewContent = null; _umbracoContext = null; + //ensure not to dispose this! Application = null; ContentCache = null; MediaCache = null; diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index f7df4bab03..a8fdb04479 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -61,7 +61,11 @@ namespace Umbraco.Web // create the UmbracoContext singleton, one per request, and assign // NOTE: we assign 'true' to ensure the context is replaced if it is already set (i.e. during app startup) - UmbracoContext.EnsureContext(httpContext, ApplicationContext.Current, true); + UmbracoContext.EnsureContext( + httpContext, + ApplicationContext.Current, + new WebSecurity(httpContext, ApplicationContext.Current), + true); } /// @@ -173,6 +177,11 @@ namespace Umbraco.Web } app.Context.User = principal; Thread.CurrentPrincipal = principal; + + //This is a back office request, we will also set the culture/ui culture + Thread.CurrentThread.CurrentCulture = + Thread.CurrentThread.CurrentUICulture = + new System.Globalization.CultureInfo(identity.Culture); } } diff --git a/src/Umbraco.Web/WebApi/Binders/ContentItemBaseBinder.cs b/src/Umbraco.Web/WebApi/Binders/ContentItemBaseBinder.cs index 9c0c589b79..f0b805ae22 100644 --- a/src/Umbraco.Web/WebApi/Binders/ContentItemBaseBinder.cs +++ b/src/Umbraco.Web/WebApi/Binders/ContentItemBaseBinder.cs @@ -18,6 +18,7 @@ using Newtonsoft.Json.Serialization; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Security; using Umbraco.Web.WebApi.Filters; using IModelBinder = System.Web.Http.ModelBinding.IModelBinder; using ModelBindingContext = System.Web.Http.ModelBinding.ModelBindingContext; @@ -90,7 +91,11 @@ namespace Umbraco.Web.WebApi.Binders var request = actionContext.Request; //IMPORTANT!!! We need to ensure the umbraco context here because this is running in an async thread - UmbracoContext.EnsureContext(request.Properties["MS_HttpContext"] as HttpContextBase, ApplicationContext.Current); + var httpContext = (HttpContextBase) request.Properties["MS_HttpContext"]; + UmbracoContext.EnsureContext( + httpContext, + ApplicationContext.Current, + new WebSecurity(httpContext, ApplicationContext.Current)); var content = request.Content; diff --git a/src/Umbraco.Web/WebApi/UmbracoApiController.cs b/src/Umbraco.Web/WebApi/UmbracoApiController.cs index 5fbb2942c5..152fd89cfc 100644 --- a/src/Umbraco.Web/WebApi/UmbracoApiController.cs +++ b/src/Umbraco.Web/WebApi/UmbracoApiController.cs @@ -44,6 +44,11 @@ namespace Umbraco.Web.WebApi return new Attempt(true, httpContext); } } + if (HttpContext.Current != null) + { + return new Attempt(true, new HttpContextWrapper(HttpContext.Current)); + } + return Attempt.False; } diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index 13074a7065..280f0969e2 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -25,6 +25,7 @@ using Umbraco.Web.Mvc; using Umbraco.Web.PropertyEditors; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; +using Umbraco.Web.Security; using Umbraco.Web.WebApi; using umbraco.BusinessLogic; using umbraco.businesslogic; @@ -98,7 +99,11 @@ namespace Umbraco.Web //before we do anything, we'll ensure the umbraco context //see: http://issues.umbraco.org/issue/U4-1717 - UmbracoContext.EnsureContext(new HttpContextWrapper(UmbracoApplication.Context), ApplicationContext); + var httpContext = new HttpContextWrapper(UmbracoApplication.Context); + UmbracoContext.EnsureContext( + httpContext, + ApplicationContext, + new WebSecurity(httpContext, ApplicationContext)); } /// diff --git a/src/umbraco.businesslogic/BasePages/BasePage.cs b/src/umbraco.businesslogic/BasePages/BasePage.cs index b226176ae6..0512319bdb 100644 --- a/src/umbraco.businesslogic/BasePages/BasePage.cs +++ b/src/umbraco.businesslogic/BasePages/BasePage.cs @@ -184,7 +184,7 @@ namespace umbraco.BasePages var identity = HttpContext.Current.GetCurrentIdentity(); if (identity == null) return -1; - return identity.Id; + return (int)identity.Id; } // Added by NH to use with webservices authentications diff --git a/src/umbraco.businesslogic/ui.cs b/src/umbraco.businesslogic/ui.cs index 6eb950495e..fd5fe6ae99 100644 --- a/src/umbraco.businesslogic/ui.cs +++ b/src/umbraco.businesslogic/ui.cs @@ -28,7 +28,12 @@ namespace umbraco /// public static string Culture(User u) { - var langFile = getLanguageFile(u.Language); + return Culture(u.Language); + } + + internal static string Culture(string userLanguage) + { + var langFile = getLanguageFile(userLanguage); try { return langFile.SelectSingleNode("/language").Attributes.GetNamedItem("culture").Value;