using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Formatting; using System.Reflection; using System.Security.Cryptography; using System.Threading.Tasks; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.Owin; using Moq; using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Web.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.ControllerTesting; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.Testing; using Umbraco.Web; using Umbraco.Web.Editors; using Umbraco.Web.Features; using Umbraco.Web.Models.ContentEditing; using IUser = Umbraco.Core.Models.Membership.IUser; using Umbraco.Core.Mapping; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Hosting; using Umbraco.Web.Routing; using Umbraco.Core.Media; using Umbraco.Net; using Umbraco.Persistance.SqlCe; using Umbraco.Web.Models.Identity; using Umbraco.Web.Security; namespace Umbraco.Tests.Web.Controllers { [TestFixture] [UmbracoTest(Database = UmbracoTestOptions.Database.None)] public class UsersControllerTests : TestWithDatabaseBase { protected override void ComposeApplication(bool withApplication) { base.ComposeApplication(withApplication); //if (!withApplication) return; // replace the true IUserService implementation with a mock // so that each test can configure the service to their liking Composition.RegisterUnique(f => Mock.Of()); // kill the true IEntityService too Composition.RegisterUnique(f => Mock.Of()); Composition.RegisterUnique(); } [Test] public async Task Save_User() { ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { var userServiceMock = Mock.Get(ServiceContext.UserService); userServiceMock.Setup(service => service.Save(It.IsAny(), It.IsAny())) .Callback((IUser u, bool raiseEvents) => { u.Id = 1234; }); userServiceMock.Setup(service => service.GetAllUserGroups(It.IsAny())) .Returns(new[] { Mock.Of(group => group.Id == 123 && group.Alias == "writers" && group.Name == "Writers") }); userServiceMock.Setup(service => service.GetUserGroupsByAlias(It.IsAny())) .Returns(new[] { Mock.Of(group => group.Id == 123 && group.Alias == "writers" && group.Name == "Writers") }); userServiceMock.Setup(service => service.GetUserById(It.IsAny())) .Returns((int id) => id == 1234 ? new User(TestObjects.GetGlobalSettings(), 1234, "Test", "test@test.com", "test@test.com", "", new List(), new int[0], new int[0]) : null); var usersController = new UsersController( Factory.GetInstance(), umbracoContextAccessor, Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), ShortStringHelper, Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance() ); return usersController; } var userSave = new UserSave { Id = 1234, Email = "test@test.com", Username = "test@test.com", Culture = "en", Name = "Test", UserGroups = new[] { "writers" } }; var runner = new TestRunner(CtrlFactory); var response = await runner.Execute("Users", "PostSaveUser", HttpMethod.Post, new ObjectContent(userSave, new JsonMediaTypeFormatter())); var obj = JsonConvert.DeserializeObject(response.Item2); Assert.AreEqual(userSave.Name, obj.Name); Assert.AreEqual(1234, obj.Id); Assert.AreEqual(userSave.Email, obj.Email); var userGroupAliases = obj.UserGroups.Select(x => x.Alias).ToArray(); foreach (var group in userSave.UserGroups) { Assert.IsTrue(userGroupAliases.Contains(group)); } } private void MockForGetPagedUsers() { Mock.Get(Current.SqlContext) .Setup(x => x.Query()) .Returns(new Query(Current.SqlContext)); var syntax = new SqlCeSyntaxProvider(); Mock.Get(Current.SqlContext) .Setup(x => x.SqlSyntax) .Returns(syntax); var mappers = new MapperCollection(new [] { new UserMapper(new Lazy(() => Current.SqlContext), new ConcurrentDictionary>()) }); Mock.Get(Current.SqlContext) .Setup(x => x.Mappers) .Returns(mappers); } [Test] public async Task GetPagedUsers_Empty() { ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { var usersController = new UsersController( Factory.GetInstance(), umbracoContextAccessor, Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), ShortStringHelper, Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance() ); return usersController; } MockForGetPagedUsers(); var runner = new TestRunner(CtrlFactory); var response = await runner.Execute("Users", "GetPagedUsers", HttpMethod.Get); var obj = JsonConvert.DeserializeObject>(response.Item2); Assert.AreEqual(0, obj.TotalItems); } [Test] public async Task GetPagedUsers_10() { ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { //setup some mocks var userServiceMock = Mock.Get(ServiceContext.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(), It.IsAny>())) .Returns(() => users); var usersController = new UsersController( Factory.GetInstance(), umbracoContextAccessor, Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), ShortStringHelper, Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance() ); return usersController; } MockForGetPagedUsers(); var runner = new TestRunner(CtrlFactory); 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()); } [Test] public async Task GetPagedUsers_Fips() { await RunFipsTest("GetPagedUsers", mock => { var users = MockedUser.CreateMulipleUsers(10); long outVal = 10; mock.Setup(service => service.GetAll( It.IsAny(), It.IsAny(), out outVal, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) .Returns(() => users); }, response => { var obj = JsonConvert.DeserializeObject>(response.Item2); Assert.AreEqual(10, obj.TotalItems); Assert.AreEqual(10, obj.Items.Count()); }); } [Test] public async Task GetById_Fips() { const int mockUserId = 1234; var user = MockedUser.CreateUser(); await RunFipsTest("GetById", mock => { mock.Setup(service => service.GetUserById(1234)) .Returns((int i) => i == mockUserId ? user : null); }, response => { var obj = JsonConvert.DeserializeObject(response.Item2); Assert.AreEqual(user.Username, obj.Username); Assert.AreEqual(user.Email, obj.Email); }, new { controller = "Users", action = "GetById" }, $"Users/GetById/{mockUserId}"); } private async Task RunFipsTest(string action, Action> userServiceSetup, Action> verification, object routeDefaults = null, string url = null) { ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor) { //setup some mocks var userServiceMock = Mock.Get(ServiceContext.UserService); userServiceSetup(userServiceMock); var usersController = new UsersController( Factory.GetInstance(), umbracoContextAccessor, Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), ShortStringHelper, Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance() ); return usersController; } // Testing what happens if the system were configured to only use FIPS-compliant algorithms var typ = typeof(CryptoConfig); var flds = typ.GetFields(BindingFlags.Static | BindingFlags.NonPublic); var haveFld = flds.FirstOrDefault(f => f.Name == "s_haveFipsAlgorithmPolicy"); var isFld = flds.FirstOrDefault(f => f.Name == "s_fipsAlgorithmPolicy"); var originalFipsValue = CryptoConfig.AllowOnlyFipsAlgorithms; try { if (!originalFipsValue) { haveFld.SetValue(null, true); isFld.SetValue(null, true); } MockForGetPagedUsers(); var runner = new TestRunner(CtrlFactory); var response = await runner.Execute("Users", action, HttpMethod.Get, routeDefaults: routeDefaults, url: url); verification(response); } finally { if (!originalFipsValue) { haveFld.SetValue(null, false); isFld.SetValue(null, false); } } } [Test] public async Task PostUnlockUsers_When_UserIds_Not_Supplied_Expect_Ok_Response() { var usersController = CreateSut(); usersController.Request = new HttpRequestMessage(); var response = await usersController.PostUnlockUsers(new int[0]); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); } [Test] public void PostUnlockUsers_When_User_Does_Not_Exist_Expect_InvalidOperationException() { var mockUserManager = CreateMockUserManager(); var usersController = CreateSut(mockUserManager); mockUserManager.Setup(x => x.FindByIdAsync(It.IsAny())) .ReturnsAsync((BackOfficeIdentityUser) null); Assert.ThrowsAsync(async () => await usersController.PostUnlockUsers(new[] {1})); } [Test] public async Task PostUnlockUsers_When_User_Lockout_Update_Fails_Expect_Failure_Response() { var mockUserManager = CreateMockUserManager(); var usersController = CreateSut(mockUserManager); const string expectedMessage = "identity error!"; var user = new BackOfficeIdentityUser( new Mock().Object, 1, new List()) { Name = "bob" }; mockUserManager.Setup(x => x.FindByIdAsync(It.IsAny())) .ReturnsAsync(user); mockUserManager.Setup(x => x.SetLockoutEndDateAsync(user, It.IsAny())) .ReturnsAsync(IdentityResult.Failed(new IdentityError {Description = expectedMessage})); var response = await usersController.PostUnlockUsers(new[] { 1 }); Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); Assert.True(response.Headers.TryGetValues("X-Status-Reason", out var values)); Assert.True(values.Contains("Validation failed")); var responseContent = response.Content as ObjectContent; var responseValue = responseContent?.Value as HttpError; Assert.NotNull(responseValue); Assert.True(responseValue.Message.Contains(expectedMessage)); Assert.True(responseValue.Message.Contains(user.Id.ToString())); } [Test] public async Task PostUnlockUsers_When_One_UserId_Supplied_Expect_User_Locked_Out_With_Correct_Response_Message() { var mockUserManager = CreateMockUserManager(); var usersController = CreateSut(mockUserManager); var user = new BackOfficeIdentityUser( new Mock().Object, 1, new List()) { Name = "bob" }; mockUserManager.Setup(x => x.FindByIdAsync(user.Id.ToString())) .ReturnsAsync(user); mockUserManager.Setup(x => x.SetLockoutEndDateAsync(user, It.IsAny())) .ReturnsAsync(IdentityResult.Success) .Verifiable(); var response = await usersController.PostUnlockUsers(new[] { user.Id }); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); var responseContent = response.Content as ObjectContent; var notifications = responseContent?.Value as SimpleNotificationModel; Assert.NotNull(notifications); Assert.AreEqual(user.Name, notifications.Message); mockUserManager.Verify(); } [Test] public async Task PostUnlockUsers_When_Multiple_UserIds_Supplied_Expect_User_Locked_Out_With_Correct_Response_Message() { var mockUserManager = CreateMockUserManager(); var usersController = CreateSut(mockUserManager); var user1 = new BackOfficeIdentityUser( new Mock().Object, 1, new List()) { Name = "bob" }; var user2 = new BackOfficeIdentityUser( new Mock().Object, 2, new List()) { Name = "alice" }; var userIdsToLock = new[] {user1.Id, user2.Id}; mockUserManager.Setup(x => x.FindByIdAsync(user1.Id.ToString())) .ReturnsAsync(user1); mockUserManager.Setup(x => x.FindByIdAsync(user2.Id.ToString())) .ReturnsAsync(user2); mockUserManager.Setup(x => x.SetLockoutEndDateAsync(user1, It.IsAny())) .ReturnsAsync(IdentityResult.Success) .Verifiable(); mockUserManager.Setup(x => x.SetLockoutEndDateAsync(user2, It.IsAny())) .ReturnsAsync(IdentityResult.Success) .Verifiable(); var response = await usersController.PostUnlockUsers(userIdsToLock); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); var responseContent = response.Content as ObjectContent; var notifications = responseContent?.Value as SimpleNotificationModel; Assert.NotNull(notifications); Assert.AreEqual(userIdsToLock.Length.ToString(), notifications.Message); mockUserManager.Verify(); } private UsersController CreateSut(IMock> mockUserManager = null) { var mockLocalizedTextService = new Mock(); mockLocalizedTextService.Setup(x => x.Localize(It.IsAny(), It.IsAny(), It.IsAny>())) .Returns((string key, CultureInfo ci, IDictionary tokens) => tokens.Aggregate("", (current, next) => current + (current == string.Empty ? "" : ",") + next.Value)); var usersController = new UsersController( Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), ServiceContext.CreatePartial(localizedTextService: mockLocalizedTextService.Object), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), ShortStringHelper, Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance()); var mockOwinContext = new Mock(); var mockUserManagerMarker = new Mock(); mockOwinContext.Setup(x => x.Get(It.IsAny())) .Returns(mockUserManagerMarker.Object); mockUserManagerMarker.Setup(x => x.GetManager(It.IsAny())) .Returns(mockUserManager?.Object ?? CreateMockUserManager().Object); usersController.Request = new HttpRequestMessage(); usersController.Request.Properties["MS_OwinContext"] = mockOwinContext.Object; usersController.Request.Properties[HttpPropertyKeys.RequestContextKey] = new HttpRequestContext {Configuration = new HttpConfiguration()}; return usersController; } private static Mock> CreateMockUserManager() { return new Mock>( new Mock().Object, new Mock().Object, new Mock>().Object, null, null, null, null, null, null, null); } } }