using System.Security.Claims; using Microsoft.AspNetCore.SignalR; using Moq; using NUnit.Framework; using Umbraco.Cms.Api.Management.ServerEvents; using Umbraco.Cms.Core; using Umbraco.Cms.Core.ServerEvents; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Cms.Api.Management.ServerEvents; [TestFixture] public class ServerEventUserManagerTests { [Test] public async Task AssignsUserToEventSourceGroup() { var userKey = Guid.NewGuid(); var user = CreateFakeUser(userKey); var authorizationService = CreateServeEventAuthorizationService(new FakeAuthorizer(["source"])); var mocks = CreateHubContextMocks(); // Add a connection to the user var connection = "connection1"; var connectionManager = new UserConnectionManager(); connectionManager.AddConnection(userKey, connection); var sut = new ServerEventUserManager(connectionManager, authorizationService, mocks.HubContextMock.Object); await sut.AssignToGroupsAsync(user, connection); // Ensure AddToGroupAsync was called once, and only once with the expected parameters. mocks.GroupManagerMock.Verify(x => x.AddToGroupAsync(connection, "source", It.IsAny()), Times.Once); mocks.GroupManagerMock.Verify(x => x.AddToGroupAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Test] public async Task DoesNotAssignUserToEventSourceGroupWhenUnauthorized() { var userKey = Guid.NewGuid(); var user = CreateFakeUser(userKey); var authorizationService = CreateServeEventAuthorizationService(new FakeAuthorizer(["source"], (_, _) => false)); var mocks = CreateHubContextMocks(); // Add a connection to the user var connection = "connection1"; var connectionManager = new UserConnectionManager(); connectionManager.AddConnection(userKey, connection); var sut = new ServerEventUserManager(connectionManager, authorizationService, mocks.HubContextMock.Object); await sut.AssignToGroupsAsync(user, connection); // Ensure AddToGroupAsync was never called. mocks.GroupManagerMock.Verify(x => x.AddToGroupAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [Test] public async Task RefreshGroupsAsyncRefreshesUserGroups() { var userKey = Guid.NewGuid(); var user = CreateFakeUser(userKey); var allowedSource = "allowedSource"; var disallowedSource = "NotAllowed"; var authorizationService = CreateServeEventAuthorizationService(new FakeAuthorizer([allowedSource]), new FakeAuthorizer([disallowedSource], (_, _) => false)); var mocks = CreateHubContextMocks(); // Add a connection to the user var connection = "connection1"; var connectionManager = new UserConnectionManager(); connectionManager.AddConnection(userKey, connection); var sut = new ServerEventUserManager(connectionManager, authorizationService, mocks.HubContextMock.Object); await sut.RefreshGroupsAsync(user); // Ensure AddToGroupAsync was called once, and only once with the expected parameters. mocks.GroupManagerMock.Verify(x => x.AddToGroupAsync(connection, allowedSource, It.IsAny()), Times.Once); mocks.GroupManagerMock.Verify(x => x.AddToGroupAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); // Ensure RemoveToGroup was called for the disallowed source, and only the disallowed source. mocks.GroupManagerMock.Verify(x => x.RemoveFromGroupAsync(connection, disallowedSource, It.IsAny()), Times.Once()); mocks.GroupManagerMock.Verify(x => x.RemoveFromGroupAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); } [Test] public async Task RefreshUserGroupsDoesNothingIfNoConnections() { var userKey = Guid.NewGuid(); var user = CreateFakeUser(userKey); var authorizationService = CreateServeEventAuthorizationService(new FakeAuthorizer(["source"]), new FakeAuthorizer(["disallowedSource"], (_, _) => false)) ?? throw new ArgumentNullException("CreateServeEventAuthorizationService(new FakeAuthorizer([\"source\"]), new FakeAuthorizer([\"disallowedSource\"], (_, _) => false))"); var mocks = CreateHubContextMocks(); var connectionManager = new UserConnectionManager(); var sut = new ServerEventUserManager(connectionManager, authorizationService, mocks.HubContextMock.Object); await sut.RefreshGroupsAsync(user); // Ensure AddToGroupAsync was never called. mocks.GroupManagerMock.Verify(x => x.AddToGroupAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } private ClaimsPrincipal CreateFakeUser(Guid key) => new(new ClaimsIdentity([ // This is the claim that's used to store the ID new Claim(Constants.Security.OpenIdDictSubClaimType, key.ToString()) ])); private IServerEventAuthorizationService CreateServeEventAuthorizationService(params IEnumerable authorizers) => new ServerEventAuthorizationService(new EventSourceAuthorizerCollection(() => authorizers)); private (Mock HubMock, Mock> HubClientsMock, Mock GroupManagerMock, Mock> HubContextMock) CreateHubContextMocks() { var hubMock = new Mock(); var hubClients = new Mock>(); hubClients.Setup(x => x.All).Returns(hubMock.Object); var groupManagerMock = new Mock(); var hubContext = new Mock>(); hubContext.Setup(x => x.Clients).Returns(hubClients.Object); hubContext.Setup(x => x.Groups).Returns(groupManagerMock.Object); return (hubMock, hubClients, groupManagerMock, hubContext); } }