From a25f68ae90b3074deb20a67b43f61d07610511bd Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Fri, 26 Feb 2010 03:09:52 -0800 Subject: [PATCH] Updated Admin filter and access control AdminController convention or [Admin] attribute activates both the security check and "TheAdmin" theme --HG-- branch : dev --- src/Orchard.Tests/Orchard.Tests.csproj | 3 +- src/Orchard.Tests/Stubs/StubHttpContext.cs | 7 ++ ...ts.cs => AdminAuthorizationFilterTests.cs} | 64 +++++++------------ .../UI/Admin/AdminThemeSelectorTests.cs | 58 +++++++++++++++++ .../Controllers/BlogAdminController.cs | 3 +- .../Controllers/BlogPostAdminController.cs | 3 +- .../UI/Admin/AdminAuthorizationFilter.cs | 23 +++++-- src/Orchard/UI/Admin/AdminThemeSelector.cs | 27 ++++---- 8 files changed, 123 insertions(+), 65 deletions(-) rename src/Orchard.Tests/UI/Admin/{AdminAttributeTests.cs => AdminAuthorizationFilterTests.cs} (55%) create mode 100644 src/Orchard.Tests/UI/Admin/AdminThemeSelectorTests.cs diff --git a/src/Orchard.Tests/Orchard.Tests.csproj b/src/Orchard.Tests/Orchard.Tests.csproj index fc3a13ca9..e2ed9d66c 100644 --- a/src/Orchard.Tests/Orchard.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Tests.csproj @@ -156,7 +156,8 @@ - + + diff --git a/src/Orchard.Tests/Stubs/StubHttpContext.cs b/src/Orchard.Tests/Stubs/StubHttpContext.cs index 06ea5e955..b2becb8f4 100644 --- a/src/Orchard.Tests/Stubs/StubHttpContext.cs +++ b/src/Orchard.Tests/Stubs/StubHttpContext.cs @@ -1,8 +1,11 @@ +using System.Collections; +using System.Collections.Generic; using System.Web; namespace Orchard.Tests.Stubs { public class StubHttpContext : HttpContextBase { private readonly string _appRelativeCurrentExecutionFilePath; + private readonly IDictionary _items = new Dictionary(); public StubHttpContext() { _appRelativeCurrentExecutionFilePath = "~/yadda"; @@ -16,6 +19,10 @@ namespace Orchard.Tests.Stubs { get { return new StupHttpRequest(_appRelativeCurrentExecutionFilePath); } } + public override IDictionary Items { + get { return _items; } + } + public class StupHttpRequest : HttpRequestBase { private readonly string _appRelativeCurrentExecutionFilePath; diff --git a/src/Orchard.Tests/UI/Admin/AdminAttributeTests.cs b/src/Orchard.Tests/UI/Admin/AdminAuthorizationFilterTests.cs similarity index 55% rename from src/Orchard.Tests/UI/Admin/AdminAttributeTests.cs rename to src/Orchard.Tests/UI/Admin/AdminAuthorizationFilterTests.cs index 18c33325d..8044db5f6 100644 --- a/src/Orchard.Tests/UI/Admin/AdminAttributeTests.cs +++ b/src/Orchard.Tests/UI/Admin/AdminAuthorizationFilterTests.cs @@ -1,22 +1,19 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web.Mvc; +using System.Web.Mvc; +using System.Web.Routing; using Moq; using NUnit.Framework; using Orchard.Localization; using Orchard.Security; -using Orchard.Security.Permissions; +using Orchard.Tests.Stubs; using Orchard.UI.Admin; namespace Orchard.Tests.UI.Admin { [TestFixture] - public class AdminAttributeTests { + public class AdminAuthorizationFilterTests { - private static AuthorizationContext GetAuthorizationContext() { + private static AuthorizationContext GetAuthorizationContext() where TController : ControllerBase, new() { var controllerDescriptor = new ReflectedControllerDescriptor(typeof(TController)); - var controllerContext = new ControllerContext(); + var controllerContext = new ControllerContext(new StubHttpContext(), new RouteData(), new TController()); return new AuthorizationContext( controllerContext, controllerDescriptor.FindAction(controllerContext, "Index")); @@ -40,56 +37,39 @@ namespace Orchard.Tests.UI.Admin { Assert.That(authorizationContext.Result, Is.Null); } - [Test] - public void AdminRequestShouldRequirePermission() { - var authorizationContext = GetAuthorizationContext(); + private static void TestActionThatShouldRequirePermission() where TController : ControllerBase, new() { + var authorizationContext = GetAuthorizationContext(); var filter = new AdminAuthorizationFilter(GetAuthorizer(false)); filter.OnAuthorization(authorizationContext); Assert.That(authorizationContext.Result, Is.InstanceOf()); + Assert.That(AdminThemeSelector.IsApplied(authorizationContext.RequestContext), Is.True); - var authorizationContext2 = GetAuthorizationContext(); + var authorizationContext2 = GetAuthorizationContext(); var filter2 = new AdminAuthorizationFilter(GetAuthorizer(true)); filter2.OnAuthorization(authorizationContext2); Assert.That(authorizationContext2.Result, Is.Null); + Assert.That(AdminThemeSelector.IsApplied(authorizationContext2.RequestContext), Is.True); + } + + + [Test] + public void AdminRequestShouldRequirePermission() { + TestActionThatShouldRequirePermission(); } [Test] public void NormalWithAttribRequestShouldRequirePermission() { - var authorizationContext = GetAuthorizationContext(); - var filter = new AdminAuthorizationFilter(GetAuthorizer(false)); - filter.OnAuthorization(authorizationContext); - Assert.That(authorizationContext.Result, Is.InstanceOf()); - - var authorizationContext2 = GetAuthorizationContext(); - var filter2 = new AdminAuthorizationFilter(GetAuthorizer(true)); - filter2.OnAuthorization(authorizationContext2); - Assert.That(authorizationContext2.Result, Is.Null); + TestActionThatShouldRequirePermission(); } - + [Test] public void NormalWithActionAttribRequestShouldRequirePermission() { - var authorizationContext = GetAuthorizationContext(); - var filter = new AdminAuthorizationFilter(GetAuthorizer(false)); - filter.OnAuthorization(authorizationContext); - Assert.That(authorizationContext.Result, Is.InstanceOf()); - - var authorizationContext2 = GetAuthorizationContext(); - var filter2 = new AdminAuthorizationFilter(GetAuthorizer(true)); - filter2.OnAuthorization(authorizationContext2); - Assert.That(authorizationContext2.Result, Is.Null); + TestActionThatShouldRequirePermission(); } [Test] public void InheritedAttribRequestShouldRequirePermission() { - var authorizationContext = GetAuthorizationContext(); - var filter = new AdminAuthorizationFilter(GetAuthorizer(false)); - filter.OnAuthorization(authorizationContext); - Assert.That(authorizationContext.Result, Is.InstanceOf()); - - var authorizationContext2 = GetAuthorizationContext(); - var filter2 = new AdminAuthorizationFilter(GetAuthorizer(true)); - filter2.OnAuthorization(authorizationContext2); - Assert.That(authorizationContext2.Result, Is.Null); + TestActionThatShouldRequirePermission(); } } @@ -118,7 +98,7 @@ namespace Orchard.Tests.UI.Admin { return View(); } } - + [Admin] public class BaseWithAttribController : Controller { public ActionResult Something() { diff --git a/src/Orchard.Tests/UI/Admin/AdminThemeSelectorTests.cs b/src/Orchard.Tests/UI/Admin/AdminThemeSelectorTests.cs new file mode 100644 index 000000000..56ffc535a --- /dev/null +++ b/src/Orchard.Tests/UI/Admin/AdminThemeSelectorTests.cs @@ -0,0 +1,58 @@ +using System.Web.Routing; +using NUnit.Framework; +using Orchard.Tests.Stubs; +using Orchard.UI.Admin; + +namespace Orchard.Tests.UI.Admin { + [TestFixture] + public class AdminThemeSelectorTests { + [Test] + public void IsAppliedShouldBeFalseByDefault() { + var context = new RequestContext(new StubHttpContext(), new RouteData()); + var isApplied = AdminThemeSelector.IsApplied(context); + Assert.That(isApplied, Is.False); + } + + [Test] + public void IsAppliedShouldBeTrueAfterBeingApplied() { + var context = new RequestContext(new StubHttpContext(), new RouteData()); + Assert.That(AdminThemeSelector.IsApplied(context), Is.False); + AdminThemeSelector.Apply(context); + Assert.That(AdminThemeSelector.IsApplied(context), Is.True); + } + + + [Test] + public void IsAppliedIsFalseOnNewContext() { + var context = new RequestContext(new StubHttpContext(), new RouteData()); + Assert.That(AdminThemeSelector.IsApplied(context), Is.False); + AdminThemeSelector.Apply(context); + Assert.That(AdminThemeSelector.IsApplied(context), Is.True); + context = new RequestContext(new StubHttpContext(), new RouteData()); + Assert.That(AdminThemeSelector.IsApplied(context), Is.False); + } + + [Test] + public void ThemeResultShouldBeNullNormally() { + var context = new RequestContext(new StubHttpContext(), new RouteData()); + + var selector = new AdminThemeSelector(); + var result = selector.GetTheme(context); + Assert.That(result, Is.Null); + } + + + [Test] + public void ThemeResultShouldBeTheAdminAt100AfterBeingSet() { + var context = new RequestContext(new StubHttpContext(), new RouteData()); + + AdminThemeSelector.Apply(context); + + var selector = new AdminThemeSelector(); + var result = selector.GetTheme(context); + Assert.That(result, Is.Not.Null); + Assert.That(result.ThemeName, Is.EqualTo("TheAdmin")); + Assert.That(result.Priority, Is.EqualTo(100)); + } + } +} diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogAdminController.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogAdminController.cs index e2956ed73..593fad828 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogAdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogAdminController.cs @@ -11,10 +11,11 @@ using Orchard.Core.Common.Models; using Orchard.Localization; using Orchard.Mvc.Results; using Orchard.Settings; +using Orchard.UI.Admin; using Orchard.UI.Notify; namespace Orchard.Blogs.Controllers { - [ValidateInput(false)] + [ValidateInput(false), Admin] public class BlogAdminController : Controller, IUpdateModel { private readonly IBlogService _blogService; private readonly IBlogPostService _blogPostService; diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogPostAdminController.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogPostAdminController.cs index 6e7a0ed42..7ac8aaa99 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogPostAdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogPostAdminController.cs @@ -6,10 +6,11 @@ using Orchard.Blogs.ViewModels; using Orchard.ContentManagement; using Orchard.Localization; using Orchard.Mvc.Results; +using Orchard.UI.Admin; using Orchard.UI.Notify; namespace Orchard.Blogs.Controllers { - [ValidateInput(false)] + [ValidateInput(false), Admin] public class BlogPostAdminController : Controller, IUpdateModel { private readonly IBlogService _blogService; private readonly IBlogPostService _blogPostService; diff --git a/src/Orchard/UI/Admin/AdminAuthorizationFilter.cs b/src/Orchard/UI/Admin/AdminAuthorizationFilter.cs index 7399e723e..879acac40 100644 --- a/src/Orchard/UI/Admin/AdminAuthorizationFilter.cs +++ b/src/Orchard/UI/Admin/AdminAuthorizationFilter.cs @@ -18,17 +18,17 @@ namespace Orchard.UI.Admin { public Localizer T { get; set; } public void OnAuthorization(AuthorizationContext filterContext) { - if (!IsAdmin(filterContext)) - return; + if (IsAdmin(filterContext)) { + if (!_authorizer.Authorize(StandardPermissions.AccessAdminPanel, T("Can't access the admin"))) { + filterContext.Result = new HttpUnauthorizedResult(); + } - if (!_authorizer.Authorize(StandardPermissions.AccessAdminPanel, T("Can't access the admin"))) { - filterContext.Result = new HttpUnauthorizedResult(); + AdminThemeSelector.Apply(filterContext.RequestContext); } } private static bool IsAdmin(AuthorizationContext filterContext) { - if (string.Equals(filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, "Admin", - StringComparison.InvariantCultureIgnoreCase)) { + if (IsNameAdmin(filterContext) || IsNameAdminProxy(filterContext)) { return true; } @@ -39,6 +39,17 @@ namespace Orchard.UI.Admin { return false; } + private static bool IsNameAdmin(AuthorizationContext filterContext) { + return string.Equals(filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, "Admin", + StringComparison.InvariantCultureIgnoreCase); + } + + private static bool IsNameAdminProxy(AuthorizationContext filterContext) { + return filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.StartsWith( + "AdminControllerProxy", StringComparison.InvariantCultureIgnoreCase) && + filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.Length == "AdminControllerProxy".Length + 32; + } + private static IEnumerable GetAdminAttributes(ActionDescriptor descriptor) { return descriptor.GetCustomAttributes(typeof(AdminAttribute), true) .Concat(descriptor.ControllerDescriptor.GetCustomAttributes(typeof(AdminAttribute), true)) diff --git a/src/Orchard/UI/Admin/AdminThemeSelector.cs b/src/Orchard/UI/Admin/AdminThemeSelector.cs index 63f58622d..9c53d1713 100644 --- a/src/Orchard/UI/Admin/AdminThemeSelector.cs +++ b/src/Orchard/UI/Admin/AdminThemeSelector.cs @@ -1,24 +1,23 @@ -using System.Globalization; -using System.IO; -using System.Web.Routing; -using Orchard.Settings; +using System.Web.Routing; using Orchard.Themes; namespace Orchard.UI.Admin { public class AdminThemeSelector : IThemeSelector { - private readonly ISiteService _siteService; + public ThemeSelectorResult GetTheme(RequestContext context) { + if (IsApplied(context)) { + return new ThemeSelectorResult { Priority = 100, ThemeName = "TheAdmin" }; + } - public AdminThemeSelector(ISiteService siteService) { - _siteService = siteService; + return null; } - public ThemeSelectorResult GetTheme(RequestContext context) { - var siteUrl = _siteService.GetSiteSettings().SiteUrl; - //todo: (heskew) get at the admin path in a less hacky way - if (!context.HttpContext.Request.Path.StartsWith(Path.Combine(siteUrl, "admin").Replace("\\", "/"), true, CultureInfo.InvariantCulture)) - return null; + public static void Apply(RequestContext context) { + // the value isn't important + context.HttpContext.Items[typeof(AdminThemeSelector)] = null; + } - return new ThemeSelectorResult { Priority = 100, ThemeName = "TheAdmin" }; + public static bool IsApplied(RequestContext context) { + return context.HttpContext.Items.Contains(typeof(AdminThemeSelector)); } } -} \ No newline at end of file +}