Published members cleanup (#10159)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker
* Merges PR
* Fixes up build and notes
* Implements security stamp and email confirmed for members, cleans up a bunch of repo/service level member groups stuff, shares user store code between members and users and fixes the user identity object so we arent' tracking both groups and roles.
* Security stamp for members is now working
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* merge changes
* oops
* Reducing and removing published member cache
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* oops didn't mean to comit this
* bah, far out this keeps getting recommitted. sorry
* cannot inject IPublishedMemberCache and cannot have IPublishedMember
* splits out files, fixes build
* fix tests
* removes membership provider classes
* removes membership provider classes
* updates the identity map definition
* reverts commented out lines
* reverts commented out lines
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-22 21:21:43 +10:00
|
|
|
using System;
|
2017-09-19 15:51:47 +02:00
|
|
|
using System.Collections.Generic;
|
2017-09-14 19:29:12 +02:00
|
|
|
using System.Net.Http;
|
2021-02-17 14:21:59 +01:00
|
|
|
using System.Security.Claims;
|
2017-09-14 19:29:12 +02:00
|
|
|
using System.Web;
|
|
|
|
|
using System.Web.Http;
|
|
|
|
|
using System.Web.Http.Controllers;
|
|
|
|
|
using System.Web.Http.Dispatcher;
|
|
|
|
|
using Moq;
|
2021-02-09 10:22:42 +01:00
|
|
|
using Umbraco.Cms.Core.Configuration.Models;
|
|
|
|
|
using Umbraco.Cms.Core.Models.Membership;
|
|
|
|
|
using Umbraco.Cms.Core.Models.PublishedContent;
|
|
|
|
|
using Umbraco.Cms.Core.PublishedCache;
|
|
|
|
|
using Umbraco.Cms.Core.Routing;
|
|
|
|
|
using Umbraco.Cms.Core.Security;
|
|
|
|
|
using Umbraco.Cms.Core.Services;
|
|
|
|
|
using Umbraco.Cms.Core.Web;
|
2021-02-10 14:45:44 +01:00
|
|
|
using Umbraco.Cms.Tests.Common;
|
2021-02-23 12:05:45 +01:00
|
|
|
using Umbraco.Extensions;
|
2021-02-15 10:48:14 +01:00
|
|
|
using Umbraco.Tests.TestHelpers.Entities;
|
2017-09-14 19:29:12 +02:00
|
|
|
using Umbraco.Web;
|
|
|
|
|
using Umbraco.Web.WebApi;
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Tests.TestHelpers.ControllerTesting
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Used to mock all of the services required for re-mocking and testing controllers
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// A more complete version of this is found in the Umbraco REST API project but this has the basics covered
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public abstract class TestControllerActivatorBase : DefaultHttpControllerActivator, IHttpControllerActivator
|
|
|
|
|
{
|
|
|
|
|
IHttpController IHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
|
|
|
|
|
{
|
2017-09-22 15:23:46 +02:00
|
|
|
// default
|
|
|
|
|
if (!typeof (UmbracoApiControllerBase).IsAssignableFrom(controllerType))
|
|
|
|
|
return base.Create(request, controllerDescriptor, controllerType);
|
|
|
|
|
|
|
|
|
|
var owinContext = request.TryGetOwinContext().Result;
|
2017-09-23 10:08:18 +02:00
|
|
|
|
2017-09-22 15:23:46 +02:00
|
|
|
var mockedUserService = Mock.Of<IUserService>();
|
|
|
|
|
var mockedContentService = Mock.Of<IContentService>();
|
|
|
|
|
var mockedMediaService = Mock.Of<IMediaService>();
|
|
|
|
|
var mockedEntityService = Mock.Of<IEntityService>();
|
2018-03-27 17:59:53 +02:00
|
|
|
var mockedMemberService = Mock.Of<IMemberService>();
|
|
|
|
|
var mockedMemberTypeService = Mock.Of<IMemberTypeService>();
|
|
|
|
|
var mockedDataTypeService = Mock.Of<IDataTypeService>();
|
|
|
|
|
var mockedContentTypeService = Mock.Of<IContentTypeService>();
|
2017-09-22 15:23:46 +02:00
|
|
|
|
2019-01-04 16:02:10 +01:00
|
|
|
var serviceContext = ServiceContext.CreatePartial(
|
2017-09-22 15:23:46 +02:00
|
|
|
userService: mockedUserService,
|
|
|
|
|
contentService: mockedContentService,
|
|
|
|
|
mediaService: mockedMediaService,
|
|
|
|
|
entityService: mockedEntityService,
|
2018-03-27 17:59:53 +02:00
|
|
|
memberService: mockedMemberService,
|
|
|
|
|
memberTypeService: mockedMemberTypeService,
|
|
|
|
|
dataTypeService: mockedDataTypeService,
|
|
|
|
|
contentTypeService: mockedContentTypeService,
|
2019-01-17 13:20:19 +11:00
|
|
|
localizedTextService:Mock.Of<ILocalizedTextService>());
|
2017-09-22 15:23:46 +02:00
|
|
|
|
2020-09-14 14:12:38 +02:00
|
|
|
var globalSettings = new GlobalSettings();
|
2017-09-22 15:23:46 +02:00
|
|
|
|
2019-01-27 01:17:32 -05:00
|
|
|
// FIXME: v8?
|
2017-09-22 15:23:46 +02:00
|
|
|
////new app context
|
|
|
|
|
//var dbCtx = new Mock<DatabaseContext>(Mock.Of<IDatabaseFactory>(), Mock.Of<ILogger>(), Mock.Of<ISqlSyntaxProvider>(), "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<ILogger>(), Mock.Of<IProfiler>()),
|
|
|
|
|
// true);
|
|
|
|
|
|
2018-04-06 13:51:54 +10:00
|
|
|
var httpContextItems = new Dictionary<string, object>
|
|
|
|
|
{
|
|
|
|
|
//add the special owin environment to the httpcontext items, this is how the GetOwinContext works
|
|
|
|
|
["owin.Environment"] = new Dictionary<string, object>()
|
|
|
|
|
};
|
|
|
|
|
|
2017-09-22 15:23:46 +02:00
|
|
|
//httpcontext with an auth'd user
|
|
|
|
|
var httpContext = Mock.Of<HttpContextBase>(
|
|
|
|
|
http => http.User == owinContext.Authentication.User
|
2017-09-23 10:08:18 +02:00
|
|
|
//ensure the request exists with a cookies collection
|
2018-09-06 17:37:47 +10:00
|
|
|
&& http.Request == Mock.Of<HttpRequestBase>(r => r.Cookies == new HttpCookieCollection()
|
|
|
|
|
&& r.RequestContext == new System.Web.Routing.RequestContext
|
|
|
|
|
{
|
|
|
|
|
RouteData = new System.Web.Routing.RouteData()
|
|
|
|
|
})
|
2017-09-23 10:08:18 +02:00
|
|
|
//ensure the request exists with an items collection
|
2018-04-06 13:51:54 +10:00
|
|
|
&& http.Items == httpContextItems);
|
2017-09-22 15:23:46 +02:00
|
|
|
//chuck it into the props since this is what MS does when hosted and it's needed there
|
2017-09-23 10:08:18 +02:00
|
|
|
request.Properties["MS_HttpContext"] = httpContext;
|
2017-09-22 15:23:46 +02:00
|
|
|
|
2021-02-17 14:21:59 +01:00
|
|
|
var backofficeIdentity = (ClaimsIdentity) owinContext.Authentication.User.Identity;
|
2017-09-22 15:23:46 +02:00
|
|
|
|
2020-10-21 16:51:00 +11:00
|
|
|
var backofficeSecurity = new Mock<IBackOfficeSecurity>();
|
2017-09-22 15:23:46 +02:00
|
|
|
|
|
|
|
|
//mock CurrentUser
|
|
|
|
|
var groups = new List<ReadOnlyUserGroup>();
|
2021-02-17 10:11:04 +01:00
|
|
|
for (var index = 0; index < backofficeIdentity.GetRoles().Length; index++)
|
2017-09-22 15:23:46 +02:00
|
|
|
{
|
2021-02-17 10:11:04 +01:00
|
|
|
var role = backofficeIdentity.GetRoles()[index];
|
2017-09-22 15:23:46 +02:00
|
|
|
groups.Add(new ReadOnlyUserGroup(index + 1, role, "icon-user", null, null, role, new string[0], new string[0]));
|
2017-09-14 19:29:12 +02:00
|
|
|
}
|
2020-06-01 16:40:04 +10:00
|
|
|
var mockUser = MockedUser.GetUserMock();
|
|
|
|
|
mockUser.Setup(x => x.IsApproved).Returns(true);
|
|
|
|
|
mockUser.Setup(x => x.IsLockedOut).Returns(false);
|
2021-02-17 10:11:04 +01:00
|
|
|
mockUser.Setup(x => x.AllowedSections).Returns(backofficeIdentity.GetAllowedApplications());
|
2020-06-01 16:40:04 +10:00
|
|
|
mockUser.Setup(x => x.Groups).Returns(groups);
|
|
|
|
|
mockUser.Setup(x => x.Email).Returns("admin@admin.com");
|
2021-02-17 10:11:04 +01:00
|
|
|
mockUser.Setup(x => x.Id).Returns((int)backofficeIdentity.GetId());
|
2020-06-01 16:40:04 +10:00
|
|
|
mockUser.Setup(x => x.Language).Returns("en");
|
2021-02-17 10:11:04 +01:00
|
|
|
mockUser.Setup(x => x.Name).Returns(backofficeIdentity.GetRealName());
|
|
|
|
|
mockUser.Setup(x => x.StartContentIds).Returns(backofficeIdentity.GetStartContentNodes());
|
|
|
|
|
mockUser.Setup(x => x.StartMediaIds).Returns(backofficeIdentity.GetStartMediaNodes());
|
|
|
|
|
mockUser.Setup(x => x.Username).Returns(backofficeIdentity.GetUsername());
|
2020-09-22 10:01:00 +02:00
|
|
|
backofficeSecurity.Setup(x => x.CurrentUser)
|
2020-06-01 16:40:04 +10:00
|
|
|
.Returns(mockUser.Object);
|
2017-09-22 15:23:46 +02:00
|
|
|
|
|
|
|
|
//mock Validate
|
2020-09-22 10:01:00 +02:00
|
|
|
backofficeSecurity.Setup(x => x.UserHasSectionAccess(It.IsAny<string>(), It.IsAny<IUser>()))
|
2017-09-22 15:23:46 +02:00
|
|
|
.Returns(() => true);
|
|
|
|
|
|
2018-04-27 11:38:50 +10:00
|
|
|
var publishedSnapshot = new Mock<IPublishedSnapshot>();
|
2017-10-31 12:50:30 +01:00
|
|
|
publishedSnapshot.Setup(x => x.Members).Returns(Mock.Of<IPublishedMemberCache>());
|
2017-10-31 12:48:24 +01:00
|
|
|
var publishedSnapshotService = new Mock<IPublishedSnapshotService>();
|
|
|
|
|
publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny<string>())).Returns(publishedSnapshot.Object);
|
2017-09-22 15:23:46 +02:00
|
|
|
|
|
|
|
|
var umbracoContextAccessor = Umbraco.Web.Composing.Current.UmbracoContextAccessor;
|
|
|
|
|
|
2020-02-13 13:57:39 +01:00
|
|
|
var httpContextAccessor = TestHelper.GetHttpContextAccessor(httpContext);
|
|
|
|
|
var umbCtx = new UmbracoContext(httpContextAccessor,
|
2017-10-31 12:48:24 +01:00
|
|
|
publishedSnapshotService.Object,
|
2020-09-22 10:01:00 +02:00
|
|
|
backofficeSecurity.Object,
|
2018-04-06 13:51:54 +10:00
|
|
|
globalSettings,
|
2020-04-03 11:03:06 +11:00
|
|
|
TestHelper.GetHostingEnvironment(),
|
2019-12-04 14:03:39 +01:00
|
|
|
new TestVariationContextAccessor(),
|
2020-02-19 09:26:51 +01:00
|
|
|
TestHelper.UriUtility,
|
|
|
|
|
new AspNetCookieManager(httpContextAccessor));
|
2019-02-14 12:11:06 +01:00
|
|
|
|
2019-02-15 12:19:42 +11:00
|
|
|
//replace it
|
|
|
|
|
umbracoContextAccessor.UmbracoContext = umbCtx;
|
2017-09-23 10:08:18 +02:00
|
|
|
|
2017-09-22 15:23:46 +02:00
|
|
|
var urlHelper = new Mock<IUrlProvider>();
|
2020-02-14 13:04:49 +01:00
|
|
|
urlHelper.Setup(provider => provider.GetUrl(It.IsAny<IPublishedContent>(), It.IsAny<UrlMode>(), It.IsAny<string>(), It.IsAny<Uri>()))
|
2018-12-18 22:02:39 +11:00
|
|
|
.Returns(UrlInfo.Url("/hello/world/1234"));
|
2017-09-22 15:23:46 +02:00
|
|
|
|
2020-03-03 11:59:17 +01:00
|
|
|
return CreateController(controllerType, request, umbracoContextAccessor);
|
2017-09-14 19:29:12 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-03 11:59:17 +01:00
|
|
|
protected abstract ApiController CreateController(Type controllerType, HttpRequestMessage msg, IUmbracoContextAccessor umbracoContextAccessor);
|
2017-09-14 19:29:12 +02:00
|
|
|
}
|
2017-09-23 10:08:18 +02:00
|
|
|
}
|