From 048112dceaaf04bdf6dc1c349656ed8c3e59b76d Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 19 May 2017 20:17:50 +1000 Subject: [PATCH] Adds framework for testing our controllers (borrowed from the rest api project) adds a couple easy ones for the user controller --- src/Umbraco.Tests/App.config | 4 +- .../Controllers/UsersControllerTests.cs | 70 +++++++++ .../AuthenticateEverythingExtensions.cs | 18 +++ .../AuthenticateEverythingMiddleware.cs | 57 +++++++ .../SpecificAssemblyResolver.cs | 21 +++ .../TestControllerActivator.cs | 22 +++ .../TestControllerActivatorBase.cs | 140 ++++++++++++++++++ .../ControllerTesting/TestRunner.cs | 76 ++++++++++ .../ControllerTesting/TestStartup.cs | 54 +++++++ .../ControllerTesting/TraceExceptionLogger.cs | 16 ++ src/Umbraco.Tests/Umbraco.Tests.csproj | 39 ++++- src/Umbraco.Tests/packages.config | 13 +- src/Umbraco.Web/Editors/UsersController.cs | 11 +- src/Umbraco.Web/Security/WebSecurity.cs | 2 +- src/Umbraco.Web/UmbracoContext.cs | 2 +- .../WebApi/AngularJsonMediaTypeFormatter.cs | 17 ++- .../UmbracoApplicationAuthorizeAttribute.cs | 6 +- 17 files changed, 544 insertions(+), 24 deletions(-) create mode 100644 src/Umbraco.Tests/Controllers/UsersControllerTests.cs create mode 100644 src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingExtensions.cs create mode 100644 src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs create mode 100644 src/Umbraco.Tests/TestHelpers/ControllerTesting/SpecificAssemblyResolver.cs create mode 100644 src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs create mode 100644 src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs create mode 100644 src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs create mode 100644 src/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs create mode 100644 src/Umbraco.Tests/TestHelpers/ControllerTesting/TraceExceptionLogger.cs diff --git a/src/Umbraco.Tests/App.config b/src/Umbraco.Tests/App.config index 6500cd3741..ef5d8133f6 100644 --- a/src/Umbraco.Tests/App.config +++ b/src/Umbraco.Tests/App.config @@ -156,7 +156,7 @@ - + @@ -172,7 +172,7 @@ - + diff --git a/src/Umbraco.Tests/Controllers/UsersControllerTests.cs b/src/Umbraco.Tests/Controllers/UsersControllerTests.cs new file mode 100644 index 0000000000..45a4979377 --- /dev/null +++ b/src/Umbraco.Tests/Controllers/UsersControllerTests.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Formatting; +using System.Net.Http.Headers; +using System.Text; +using System.Web.Configuration; +using System.Web.Http; +using System.Web.Http.SelfHost; +using AutoMapper; +using Examine.Providers; +using Microsoft.Owin.Testing; +using Moq; +using Newtonsoft.Json; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.ControllerTesting; +using Umbraco.Tests.TestHelpers.Entities; +using Umbraco.Web.Editors; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Models.Mapping; +using Umbraco.Web.WebApi; + +namespace Umbraco.Tests.Controllers +{ + [DatabaseTestBehavior(DatabaseBehavior.NoDatabasePerFixture)] + [RequiresAutoMapperMappings] + [TestFixture] + public class UsersControllerTests : BaseDatabaseFactoryTest + { + [Test] + public async void GetPagedUsers_Empty() + { + var runner = new TestRunner((message, helper) => new UsersController(helper.UmbracoContext)); + var response = await runner.Execute("Users", "GetPagedUsers", HttpMethod.Get); + + var obj = JsonConvert.DeserializeObject>(response.Item2); + Assert.AreEqual(0, obj.TotalItems); + } + + [Test] + public async void GetPagedUsers_10() + { + var runner = new TestRunner((message, helper) => + { + //setup some mocks + var userServiceMock = Mock.Get(helper.UmbracoContext.Application.Services.UserService); + var users = MockedUser.CreateMulipleUsers(10); + long outVal = 10; + userServiceMock.Setup(service => service.GetAll(It.IsAny(), It.IsAny(), out outVal, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(() => users); + + return new UsersController(helper.UmbracoContext); + }); + var response = await runner.Execute("Users", "GetPagedUsers", HttpMethod.Get); + + var obj = JsonConvert.DeserializeObject>(response.Item2); + Assert.AreEqual(10, obj.TotalItems); + Assert.AreEqual(10, obj.Items.Count()); + } + } +} diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingExtensions.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingExtensions.cs new file mode 100644 index 0000000000..f7d457f0d1 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingExtensions.cs @@ -0,0 +1,18 @@ +using System; +using Microsoft.Owin.Extensions; +using Owin; + +namespace Umbraco.Tests.TestHelpers.ControllerTesting +{ + public static class AuthenticateEverythingExtensions + { + public static IAppBuilder AuthenticateEverything(this IAppBuilder app) + { + if (app == null) + throw new ArgumentNullException("app"); + app.Use(typeof(AuthenticateEverythingMiddleware), (object)app, (object)new AuthenticateEverythingMiddleware.AuthenticateEverythingAuthenticationOptions()); + app.UseStageMarker(PipelineStage.Authenticate); + return app; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs new file mode 100644 index 0000000000..2dbfa757f2 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Owin; +using Microsoft.Owin.Security; +using Microsoft.Owin.Security.Infrastructure; +using Owin; +using Umbraco.Core.Security; + +namespace Umbraco.Tests.TestHelpers.ControllerTesting +{ + public class AuthenticateEverythingMiddleware : AuthenticationMiddleware + { + public AuthenticateEverythingMiddleware(OwinMiddleware next, IAppBuilder app, AuthenticationOptions options) + : base(next, options) + { + } + + protected override AuthenticationHandler CreateHandler() + { + return new AuthenticateEverythingHandler(); + } + + public class AuthenticateEverythingHandler : AuthenticationHandler + { + protected override Task AuthenticateCoreAsync() + { + var identity = new UmbracoBackOfficeIdentity( + new UserData(Guid.NewGuid().ToString()) + { + Id = 0, + Roles = new[] { "admin" }, + AllowedApplications = new[] { "content", "media", "members" }, + Culture = "en-US", + RealName = "Admin", + StartContentNode = -1, + StartMediaNode = -1, + Username = "admin" + }); + + return Task.FromResult(new AuthenticationTicket(identity, + new AuthenticationProperties() + { + ExpiresUtc = DateTime.Now.AddDays(1) + })); + } + } + + public class AuthenticateEverythingAuthenticationOptions : AuthenticationOptions + { + public AuthenticateEverythingAuthenticationOptions() + : base("AuthenticateEverything") + { + AuthenticationMode = AuthenticationMode.Active; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/SpecificAssemblyResolver.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/SpecificAssemblyResolver.cs new file mode 100644 index 0000000000..b301952c92 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/SpecificAssemblyResolver.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Reflection; +using System.Web.Http.Dispatcher; + +namespace Umbraco.Tests.TestHelpers.ControllerTesting +{ + public class SpecificAssemblyResolver : IAssembliesResolver + { + private readonly Assembly[] _assemblies; + + public SpecificAssemblyResolver(Assembly[] assemblies) + { + _assemblies = assemblies; + } + + public ICollection GetAssemblies() + { + return _assemblies; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs new file mode 100644 index 0000000000..81cdeccbfe --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs @@ -0,0 +1,22 @@ +using System; +using System.Net.Http; +using System.Web.Http; +using Umbraco.Web; + +namespace Umbraco.Tests.TestHelpers.ControllerTesting +{ + public class TestControllerActivator : TestControllerActivatorBase + { + private readonly Func _factory; + + public TestControllerActivator(Func factory) + { + _factory = factory; + } + + protected override ApiController CreateController(Type controllerType, HttpRequestMessage msg, UmbracoHelper helper) + { + return _factory(msg, helper); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs new file mode 100644 index 0000000000..bebf6ab1e2 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -0,0 +1,140 @@ +using System; +using System.Linq; +using System.Net.Http; +using System.Web; +using System.Web.Http; +using System.Web.Http.Controllers; +using System.Web.Http.Dispatcher; +using System.Web.Security; +using Moq; +using Semver; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Dictionary; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Profiling; +using Umbraco.Core.Security; +using Umbraco.Core.Services; +using Umbraco.Web; +using Umbraco.Web.Routing; +using Umbraco.Web.Security; +using Umbraco.Web.WebApi; + +namespace Umbraco.Tests.TestHelpers.ControllerTesting +{ + /// + /// Used to mock all of the services required for re-mocking and testing controllers + /// + /// + /// A more complete version of this is found in the Umbraco REST API project but this has the basics covered + /// + public abstract class TestControllerActivatorBase : DefaultHttpControllerActivator, IHttpControllerActivator + { + IHttpController IHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) + { + if (typeof(UmbracoApiControllerBase).IsAssignableFrom(controllerType)) + { + var owinContext = request.GetOwinContext(); + + var mockedUserService = Mock.Of(); + + var mockedMigrationService = new Mock(); + //set it up to return anything so that the app ctx is 'Configured' + mockedMigrationService.Setup(x => x.FindEntry(It.IsAny(), It.IsAny())).Returns(Mock.Of()); + + var serviceContext = new ServiceContext( + userService: mockedUserService, + migrationEntryService: mockedMigrationService.Object); + + //ensure the configuration matches the current version for tests + SettingsForTests.ConfigurationStatus = UmbracoVersion.GetSemanticVersion().ToSemanticString(); + + //new app context + var dbCtx = new Mock(Mock.Of(), Mock.Of(), Mock.Of(), "test"); + //ensure these are set so that the appctx is 'Configured' + dbCtx.Setup(x => x.CanConnect).Returns(true); + dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(true); + var appCtx = ApplicationContext.EnsureContext( + dbCtx.Object, + //pass in mocked services + serviceContext, + CacheHelper.CreateDisabledCacheHelper(), + new ProfilingLogger(Mock.Of(), Mock.Of()), + true); + + //httpcontext with an auth'd user + var httpContext = Mock.Of( + http => http.User == owinContext.Authentication.User + //ensure the request exists with a cookies collection + && http.Request == Mock.Of(r => r.Cookies == new HttpCookieCollection())); + //chuck it into the props since this is what MS does when hosted and it's needed there + request.Properties["MS_HttpContext"] = httpContext; + + var backofficeIdentity = (UmbracoBackOfficeIdentity)owinContext.Authentication.User.Identity; + + var webSecurity = new Mock(null, null); + + //mock CurrentUser + webSecurity.Setup(x => x.CurrentUser) + .Returns(Mock.Of(u => u.IsApproved == true + && u.IsLockedOut == false + && u.AllowedSections == backofficeIdentity.AllowedApplications + && u.Email == "admin@admin.com" + && u.Id == (int) backofficeIdentity.Id + && u.Language == "en" + && u.Name == backofficeIdentity.RealName + && u.StartContentId == backofficeIdentity.StartContentNode + && u.StartMediaId == backofficeIdentity.StartMediaNode + && u.Username == backofficeIdentity.Username)); + + //mock Validate + webSecurity.Setup(x => x.ValidateCurrentUser()) + .Returns(() => true); + webSecurity.Setup(x => x.UserHasAppAccess(It.IsAny(), It.IsAny())) + .Returns(() => true); + + var umbCtx = UmbracoContext.EnsureContext( + //set the user of the HttpContext + httpContext, + appCtx, + webSecurity.Object, + Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == UrlProviderMode.Auto.ToString())), + Enumerable.Empty(), + true); //replace it + + var urlHelper = new Mock(); + urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns("/hello/world/1234"); + + var membershipHelper = new MembershipHelper(umbCtx, Mock.Of(), Mock.Of()); + + var mockedTypedContent = Mock.Of(); + + var umbHelper = new UmbracoHelper(umbCtx, + Mock.Of(), + mockedTypedContent, + Mock.Of(), + Mock.Of(), + Mock.Of(), + new UrlProvider(umbCtx, new[] + { + urlHelper.Object + }, UrlProviderMode.Auto), + Mock.Of(), + Mock.Of(), + membershipHelper); + + return CreateController(controllerType, request, umbHelper); + } + //default + return base.Create(request, controllerDescriptor, controllerType); + } + + protected abstract ApiController CreateController(Type controllerType, HttpRequestMessage msg, UmbracoHelper helper); + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs new file mode 100644 index 0000000000..12ffda5e04 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestRunner.cs @@ -0,0 +1,76 @@ +using System; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using System.Web.Http; +using Microsoft.Owin.Testing; +using Newtonsoft.Json; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Web; +using Umbraco.Web.WebApi; + +namespace Umbraco.Tests.TestHelpers.ControllerTesting +{ + public class TestRunner + { + private readonly Func _controllerFactory; + + public TestRunner(Func controllerFactory) + { + _controllerFactory = controllerFactory; + } + + public async Task> Execute(string controllerName, string actionName, HttpMethod method) + { + var startup = new TestStartup( + configuration => + { + configuration.Routes.MapHttpRoute("Default", + routeTemplate: "{controller}/{action}/{id}", + defaults: new { controller = controllerName, action = actionName, id = RouteParameter.Optional }); + }, + _controllerFactory); + + using (var server = TestServer.Create(builder => startup.Configuration(builder))) + { + var request = new HttpRequestMessage + { + RequestUri = new Uri("http://testserver/"), + Method = method, + }; + + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + + Console.WriteLine(request); + var response = await server.HttpClient.SendAsync(request); + Console.WriteLine(response); + + string json = ""; + if (response.IsSuccessStatusCode == false) + { + WriteResponseError(response); + } + else + { + json = (await ((StreamContent)response.Content).ReadAsStringAsync()).TrimStart(AngularJsonMediaTypeFormatter.XsrfPrefix); + var deserialized = JsonConvert.DeserializeObject(json); + Console.Write(JsonConvert.SerializeObject(deserialized, Formatting.Indented)); + } + + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + return Tuple.Create(response, json); + } + } + + private static void WriteResponseError(HttpResponseMessage response) + { + var result = response.Content.ReadAsStringAsync().Result; + Console.Out.WriteLine("Http operation unsuccessfull"); + Console.Out.WriteLine(string.Format("Status: '{0}'", response.StatusCode)); + Console.Out.WriteLine(string.Format("Reason: '{0}'", response.ReasonPhrase)); + Console.Out.WriteLine(result); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs new file mode 100644 index 0000000000..1bbb03095f --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs @@ -0,0 +1,54 @@ +using System; +using System.Net.Http; +using System.Web.Http; +using System.Web.Http.Dispatcher; +using System.Web.Http.ExceptionHandling; +using System.Web.Http.Tracing; +using Owin; +using Umbraco.Web; +using Umbraco.Web.Editors; +using Umbraco.Web.WebApi; + +namespace Umbraco.Tests.TestHelpers.ControllerTesting +{ + /// + /// Startup class for the self-hosted web server works for OWIN and WebAPI + /// + public class TestStartup + { + private readonly Func _controllerFactory; + private readonly Action _initialize; + + public TestStartup(Action initialize, Func controllerFactory) + { + _controllerFactory = controllerFactory; + _initialize = initialize; + } + + public void Configuration(IAppBuilder app) + { + var httpConfig = new HttpConfiguration(); + + //TODO: Enable this if you can't see the errors produced + //var traceWriter = httpConfig.EnableSystemDiagnosticsTracing(); + //traceWriter.IsVerbose = true; + //traceWriter.MinimumLevel = TraceLevel.Debug; + + httpConfig.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; + + // Add in a simple exception tracer so we can see what is causing the 500 Internal Server Error + httpConfig.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger()); + + httpConfig.Services.Replace(typeof(IAssembliesResolver), new SpecificAssemblyResolver(new[] { typeof(UsersController).Assembly })); + httpConfig.Services.Replace(typeof(IHttpControllerActivator), new TestControllerActivator(_controllerFactory)); + httpConfig.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(httpConfig)); + + //auth everything + app.AuthenticateEverything(); + + _initialize(httpConfig); + + app.UseWebApi(httpConfig); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TraceExceptionLogger.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TraceExceptionLogger.cs new file mode 100644 index 0000000000..faf5870cbb --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TraceExceptionLogger.cs @@ -0,0 +1,16 @@ +using System.Diagnostics; +using System.Web.Http.ExceptionHandling; + +namespace Umbraco.Tests.TestHelpers.ControllerTesting +{ + /// + /// Traces any errors for WebApi to the output window + /// + public class TraceExceptionLogger : ExceptionLogger + { + public override void Log(ExceptionLoggerContext context) + { + Trace.TraceError(context.ExceptionContext.Exception.ToString()); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 498bc35c86..651ae301ce 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -76,8 +76,23 @@ ..\packages\Lucene.Net.2.9.4.1\lib\net40\Lucene.Net.dll - - ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll + + ..\packages\Microsoft.Owin.3.1.0\lib\net45\Microsoft.Owin.dll + + + ..\packages\Microsoft.Owin.Host.HttpListener.3.1.0\lib\net45\Microsoft.Owin.Host.HttpListener.dll + + + ..\packages\Microsoft.Owin.Host.SystemWeb.3.1.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll + + + ..\packages\Microsoft.Owin.Hosting.3.1.0\lib\net45\Microsoft.Owin.Hosting.dll + + + ..\packages\Microsoft.Owin.Security.3.1.0\lib\net45\Microsoft.Owin.Security.dll + + + ..\packages\Microsoft.Owin.Testing.3.1.0\lib\net45\Microsoft.Owin.Testing.dll ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll @@ -128,9 +143,14 @@ ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll - - ..\packages\Microsoft.AspNet.WebApi.SelfHost.4.0.30506.0\lib\net40\System.Web.Http.SelfHost.dll - True + + ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll + + + ..\packages\Microsoft.AspNet.WebApi.SelfHost.5.2.3\lib\net45\System.Web.Http.SelfHost.dll + + + ..\packages\Microsoft.AspNet.WebApi.Tracing.5.2.3\lib\net45\System.Web.Http.Tracing.dll ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll @@ -162,6 +182,15 @@ + + + + + + + + + diff --git a/src/Umbraco.Tests/packages.config b/src/Umbraco.Tests/packages.config index a9cbce1aad..1577212ffe 100644 --- a/src/Umbraco.Tests/packages.config +++ b/src/Umbraco.Tests/packages.config @@ -1,6 +1,5 @@  - @@ -12,10 +11,18 @@ - + + + + - + + + + + + diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index 0d48b96bd0..e34d98e902 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -37,7 +37,7 @@ namespace Umbraco.Web.Editors : base(umbracoContext) { } - + /// /// Gets a user by Id /// @@ -55,16 +55,21 @@ namespace Umbraco.Web.Editors public PagedResult GetPagedUsers( int pageNumber = 1, - int pageSize = 0, + int pageSize = 10, string orderBy = "username", Direction orderDirection = Direction.Ascending, - string[] userGroups = null, + [FromUri]string[] userGroups = null, string filter = "") { long pageIndex = pageNumber - 1; long total; var result = Services.UserService.GetAll(pageIndex, pageSize, out total, orderBy, orderDirection, null, userGroups, filter); + if (total == 0) + { + return new PagedResult(0, 0, 0); + } + return new PagedResult(total, pageNumber, pageSize) { Items = Mapper.Map>(result) diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index e459bb9eb4..05407a93e5 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -395,7 +395,7 @@ namespace Umbraco.Web.Security /// /// /// - internal bool UserHasAppAccess(string app, IUser user) + internal virtual bool UserHasAppAccess(string app, IUser user) { var apps = user.AllowedSections; return apps.Any(uApp => uApp.InvariantEquals(app)); diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index 90f4e4df03..dbb165a526 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -259,7 +259,7 @@ namespace Umbraco.Web var requestUrl = new Uri("http://localhost"); var request = GetRequestFromContext(); - if (request != null) + if (request != null && request.Url != null) { requestUrl = request.Url; } diff --git a/src/Umbraco.Web/WebApi/AngularJsonMediaTypeFormatter.cs b/src/Umbraco.Web/WebApi/AngularJsonMediaTypeFormatter.cs index 66a79376aa..8dca204597 100644 --- a/src/Umbraco.Web/WebApi/AngularJsonMediaTypeFormatter.cs +++ b/src/Umbraco.Web/WebApi/AngularJsonMediaTypeFormatter.cs @@ -21,6 +21,8 @@ namespace Umbraco.Web.WebApi public class AngularJsonMediaTypeFormatter : JsonMediaTypeFormatter { + public const string XsrfPrefix = ")]}',\n"; + /// /// This will prepend the special chars to the stream output that angular will strip /// @@ -30,24 +32,25 @@ namespace Umbraco.Web.WebApi /// /// /// - public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext) + public override async Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext) { - if (type == null) throw new ArgumentNullException("type"); if (writeStream == null) throw new ArgumentNullException("writeStream"); var effectiveEncoding = SelectCharacterEncoding(content == null ? null : content.Headers); - using (var streamWriter = new StreamWriter(writeStream, effectiveEncoding)) + using (var streamWriter = new StreamWriter(writeStream, effectiveEncoding, + //we are only writing a few chars so we don't need to allocate a large buffer + 128, + //this is important! We don't want to close the stream, the base class is in charge of stream management, we just want to write to it. + leaveOpen:true)) { //write the special encoding for angular json to the start // (see: http://docs.angularjs.org/api/ng.$http) - streamWriter.Write(")]}',\n"); + streamWriter.Write(XsrfPrefix); streamWriter.Flush(); - return base.WriteToStreamAsync(type, value, writeStream, content, transportContext); + await base.WriteToStreamAsync(type, value, writeStream, content, transportContext); } - - } } diff --git a/src/Umbraco.Web/WebApi/Filters/UmbracoApplicationAuthorizeAttribute.cs b/src/Umbraco.Web/WebApi/Filters/UmbracoApplicationAuthorizeAttribute.cs index 23e0fde1c2..c305ad3339 100644 --- a/src/Umbraco.Web/WebApi/Filters/UmbracoApplicationAuthorizeAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/UmbracoApplicationAuthorizeAttribute.cs @@ -33,10 +33,12 @@ namespace Umbraco.Web.WebApi.Filters { return true; } - - return UmbracoContext.Current.Security.CurrentUser != null + + var authorized = UmbracoContext.Current.Security.CurrentUser != null && _appNames.Any(app => UmbracoContext.Current.Security.UserHasAppAccess( app, UmbracoContext.Current.Security.CurrentUser)); + + return authorized; } } } \ No newline at end of file