Fix tests.

This commit is contained in:
crobibero 2020-06-16 14:12:40 -06:00
parent a8adbef74f
commit 774fdbd031
6 changed files with 155 additions and 64 deletions

View File

@ -62,7 +62,7 @@ namespace Jellyfin.Api.Auth
new Claim(InternalClaimTypes.Device, authorizationInfo.Device), new Claim(InternalClaimTypes.Device, authorizationInfo.Device),
new Claim(InternalClaimTypes.Client, authorizationInfo.Client), new Claim(InternalClaimTypes.Client, authorizationInfo.Client),
new Claim(InternalClaimTypes.Version, authorizationInfo.Version), new Claim(InternalClaimTypes.Version, authorizationInfo.Version),
new Claim(InternalClaimTypes.Token, authorizationInfo.Token) new Claim(InternalClaimTypes.Token, authorizationInfo.Token),
}; };
var identity = new ClaimsIdentity(claims, Scheme.Name); var identity = new ClaimsIdentity(claims, Scheme.Name);

View File

@ -38,6 +38,7 @@ namespace Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy
if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted) if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted)
{ {
context.Succeed(firstTimeSetupOrElevatedRequirement); context.Succeed(firstTimeSetupOrElevatedRequirement);
return Task.CompletedTask;
} }
var validated = ValidateClaims(context.User); var validated = ValidateClaims(context.User);

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks; using System.Threading.Tasks;
using AutoFixture; using AutoFixture;
using AutoFixture.AutoMoq; using AutoFixture.AutoMoq;
@ -9,7 +8,6 @@ using Jellyfin.Api.Auth;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -26,12 +24,6 @@ namespace Jellyfin.Api.Tests.Auth
private readonly IFixture _fixture; private readonly IFixture _fixture;
private readonly Mock<IAuthService> _jellyfinAuthServiceMock; private readonly Mock<IAuthService> _jellyfinAuthServiceMock;
private readonly Mock<IOptionsMonitor<AuthenticationSchemeOptions>> _optionsMonitorMock;
private readonly Mock<ISystemClock> _clockMock;
private readonly Mock<IServiceProvider> _serviceProviderMock;
private readonly Mock<IAuthenticationService> _authenticationServiceMock;
private readonly UrlEncoder _urlEncoder;
private readonly HttpContext _context;
private readonly CustomAuthenticationHandler _sut; private readonly CustomAuthenticationHandler _sut;
private readonly AuthenticationScheme _scheme; private readonly AuthenticationScheme _scheme;
@ -47,26 +39,23 @@ namespace Jellyfin.Api.Tests.Auth
AllowFixtureCircularDependencies(); AllowFixtureCircularDependencies();
_jellyfinAuthServiceMock = _fixture.Freeze<Mock<IAuthService>>(); _jellyfinAuthServiceMock = _fixture.Freeze<Mock<IAuthService>>();
_optionsMonitorMock = _fixture.Freeze<Mock<IOptionsMonitor<AuthenticationSchemeOptions>>>(); var optionsMonitorMock = _fixture.Freeze<Mock<IOptionsMonitor<AuthenticationSchemeOptions>>>();
_clockMock = _fixture.Freeze<Mock<ISystemClock>>(); var serviceProviderMock = _fixture.Freeze<Mock<IServiceProvider>>();
_serviceProviderMock = _fixture.Freeze<Mock<IServiceProvider>>(); var authenticationServiceMock = _fixture.Freeze<Mock<IAuthenticationService>>();
_authenticationServiceMock = _fixture.Freeze<Mock<IAuthenticationService>>();
_fixture.Register<ILoggerFactory>(() => new NullLoggerFactory()); _fixture.Register<ILoggerFactory>(() => new NullLoggerFactory());
_urlEncoder = UrlEncoder.Default; serviceProviderMock.Setup(s => s.GetService(typeof(IAuthenticationService)))
.Returns(authenticationServiceMock.Object);
_serviceProviderMock.Setup(s => s.GetService(typeof(IAuthenticationService))) optionsMonitorMock.Setup(o => o.Get(It.IsAny<string>()))
.Returns(_authenticationServiceMock.Object);
_optionsMonitorMock.Setup(o => o.Get(It.IsAny<string>()))
.Returns(new AuthenticationSchemeOptions .Returns(new AuthenticationSchemeOptions
{ {
ForwardAuthenticate = null ForwardAuthenticate = null
}); });
_context = new DefaultHttpContext HttpContext context = new DefaultHttpContext
{ {
RequestServices = _serviceProviderMock.Object RequestServices = serviceProviderMock.Object
}; };
_scheme = new AuthenticationScheme( _scheme = new AuthenticationScheme(
@ -75,24 +64,7 @@ namespace Jellyfin.Api.Tests.Auth
typeof(CustomAuthenticationHandler)); typeof(CustomAuthenticationHandler));
_sut = _fixture.Create<CustomAuthenticationHandler>(); _sut = _fixture.Create<CustomAuthenticationHandler>();
_sut.InitializeAsync(_scheme, _context).Wait(); _sut.InitializeAsync(_scheme, context).Wait();
}
[Fact]
public async Task HandleAuthenticateAsyncShouldFailWithNullUser()
{
_jellyfinAuthServiceMock.Setup(
a => a.Authenticate(
It.IsAny<HttpRequest>(),
It.IsAny<AuthenticatedAttribute>()))
.Returns((User?)null);
var authenticateResult = await _sut.AuthenticateAsync();
Assert.False(authenticateResult.Succeeded);
Assert.True(authenticateResult.None);
// TODO return when legacy API is removed.
// Assert.Equal("Invalid user", authenticateResult.Failure.Message);
} }
[Fact] [Fact]
@ -102,8 +74,7 @@ namespace Jellyfin.Api.Tests.Auth
_jellyfinAuthServiceMock.Setup( _jellyfinAuthServiceMock.Setup(
a => a.Authenticate( a => a.Authenticate(
It.IsAny<HttpRequest>(), It.IsAny<HttpRequest>()))
It.IsAny<AuthenticatedAttribute>()))
.Throws(new SecurityException(errorMessage)); .Throws(new SecurityException(errorMessage));
var authenticateResult = await _sut.AuthenticateAsync(); var authenticateResult = await _sut.AuthenticateAsync();
@ -125,10 +96,10 @@ namespace Jellyfin.Api.Tests.Auth
[Fact] [Fact]
public async Task HandleAuthenticateAsyncShouldAssignNameClaim() public async Task HandleAuthenticateAsyncShouldAssignNameClaim()
{ {
var user = SetupUser(); var authorizationInfo = SetupUser();
var authenticateResult = await _sut.AuthenticateAsync(); var authenticateResult = await _sut.AuthenticateAsync();
Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Name, user.Username)); Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Name, authorizationInfo.User.Username));
} }
[Theory] [Theory]
@ -136,10 +107,10 @@ namespace Jellyfin.Api.Tests.Auth
[InlineData(false)] [InlineData(false)]
public async Task HandleAuthenticateAsyncShouldAssignRoleClaim(bool isAdmin) public async Task HandleAuthenticateAsyncShouldAssignRoleClaim(bool isAdmin)
{ {
var user = SetupUser(isAdmin); var authorizationInfo = SetupUser(isAdmin);
var authenticateResult = await _sut.AuthenticateAsync(); var authenticateResult = await _sut.AuthenticateAsync();
var expectedRole = user.HasPermission(PermissionKind.IsAdministrator) ? UserRoles.Administrator : UserRoles.User; var expectedRole = authorizationInfo.User.HasPermission(PermissionKind.IsAdministrator) ? UserRoles.Administrator : UserRoles.User;
Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Role, expectedRole)); Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Role, expectedRole));
} }
@ -152,18 +123,18 @@ namespace Jellyfin.Api.Tests.Auth
Assert.Equal(_scheme.Name, authenticatedResult.Ticket.AuthenticationScheme); Assert.Equal(_scheme.Name, authenticatedResult.Ticket.AuthenticationScheme);
} }
private User SetupUser(bool isAdmin = false) private AuthorizationInfo SetupUser(bool isAdmin = false)
{ {
var user = _fixture.Create<User>(); var authorizationInfo = _fixture.Create<AuthorizationInfo>();
user.SetPermission(PermissionKind.IsAdministrator, isAdmin); authorizationInfo.User = _fixture.Create<User>();
authorizationInfo.User.SetPermission(PermissionKind.IsAdministrator, isAdmin);
_jellyfinAuthServiceMock.Setup( _jellyfinAuthServiceMock.Setup(
a => a.Authenticate( a => a.Authenticate(
It.IsAny<HttpRequest>(), It.IsAny<HttpRequest>()))
It.IsAny<AuthenticatedAttribute>())) .Returns(authorizationInfo);
.Returns(user);
return user; return authorizationInfo;
} }
private void AllowFixtureCircularDependencies() private void AllowFixtureCircularDependencies()

View File

@ -1,13 +1,21 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using AutoFixture; using AutoFixture;
using AutoFixture.AutoMoq; using AutoFixture.AutoMoq;
using Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy; using Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using Jellyfin.Server.Implementations.Users;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Moq; using Moq;
using Xunit; using Xunit;
@ -15,15 +23,28 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy
{ {
public class FirstTimeSetupOrElevatedHandlerTests public class FirstTimeSetupOrElevatedHandlerTests
{ {
/// <summary>
/// 127.0.0.1.
/// </summary>
private const long InternalIp = 16777343;
/// <summary>
/// 1.1.1.1.
/// </summary>
/// private const long ExternalIp = 16843009;
private readonly Mock<IConfigurationManager> _configurationManagerMock; private readonly Mock<IConfigurationManager> _configurationManagerMock;
private readonly List<IAuthorizationRequirement> _requirements; private readonly List<IAuthorizationRequirement> _requirements;
private readonly FirstTimeSetupOrElevatedHandler _sut; private readonly FirstTimeSetupOrElevatedHandler _sut;
private readonly Mock<IUserManager> _userManagerMock;
private readonly Mock<IHttpContextAccessor> _httpContextAccessor;
public FirstTimeSetupOrElevatedHandlerTests() public FirstTimeSetupOrElevatedHandlerTests()
{ {
var fixture = new Fixture().Customize(new AutoMoqCustomization()); var fixture = new Fixture().Customize(new AutoMoqCustomization());
_configurationManagerMock = fixture.Freeze<Mock<IConfigurationManager>>(); _configurationManagerMock = fixture.Freeze<Mock<IConfigurationManager>>();
_requirements = new List<IAuthorizationRequirement> { new FirstTimeSetupOrElevatedRequirement() }; _requirements = new List<IAuthorizationRequirement> { new FirstTimeSetupOrElevatedRequirement() };
_userManagerMock = fixture.Freeze<Mock<IUserManager>>();
_httpContextAccessor = fixture.Freeze<Mock<IHttpContextAccessor>>();
_sut = fixture.Create<FirstTimeSetupOrElevatedHandler>(); _sut = fixture.Create<FirstTimeSetupOrElevatedHandler>();
} }
@ -35,8 +56,15 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy
public async Task ShouldSucceedIfStartupWizardIncomplete(string userRole) public async Task ShouldSucceedIfStartupWizardIncomplete(string userRole)
{ {
SetupConfigurationManager(false); SetupConfigurationManager(false);
var user = SetupUser(userRole); var (user, claims) = SetupUser(userRole);
var context = new AuthorizationHandlerContext(_requirements, user, null);
_userManagerMock.Setup(u => u.GetUserById(It.IsAny<Guid>()))
.Returns(user);
_httpContextAccessor.Setup(h => h.HttpContext.Connection.RemoteIpAddress)
.Returns(new IPAddress(InternalIp));
var context = new AuthorizationHandlerContext(_requirements, claims, null);
await _sut.HandleAsync(context); await _sut.HandleAsync(context);
Assert.True(context.HasSucceeded); Assert.True(context.HasSucceeded);
@ -49,18 +77,42 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy
public async Task ShouldRequireAdministratorIfStartupWizardComplete(string userRole, bool shouldSucceed) public async Task ShouldRequireAdministratorIfStartupWizardComplete(string userRole, bool shouldSucceed)
{ {
SetupConfigurationManager(true); SetupConfigurationManager(true);
var user = SetupUser(userRole); var (user, claims) = SetupUser(userRole);
var context = new AuthorizationHandlerContext(_requirements, user, null);
_userManagerMock.Setup(u => u.GetUserById(It.IsAny<Guid>()))
.Returns(user);
_httpContextAccessor.Setup(h => h.HttpContext.Connection.RemoteIpAddress)
.Returns(new IPAddress(InternalIp));
var context = new AuthorizationHandlerContext(_requirements, claims, null);
await _sut.HandleAsync(context); await _sut.HandleAsync(context);
Assert.Equal(shouldSucceed, context.HasSucceeded); Assert.Equal(shouldSucceed, context.HasSucceeded);
} }
private static ClaimsPrincipal SetupUser(string role) private static (User, ClaimsPrincipal) SetupUser(string role)
{ {
var claims = new[] { new Claim(ClaimTypes.Role, role) }; var user = new User(
"jellyfin",
typeof(DefaultAuthenticationProvider).FullName,
typeof(DefaultPasswordResetProvider).FullName);
user.SetPermission(PermissionKind.IsAdministrator, role.Equals(UserRoles.Administrator, StringComparison.OrdinalIgnoreCase));
var claims = new[]
{
new Claim(ClaimTypes.Role, role),
new Claim(ClaimTypes.Name, "jellyfin"),
new Claim(InternalClaimTypes.UserId, Guid.Empty.ToString("N", CultureInfo.InvariantCulture)),
new Claim(InternalClaimTypes.DeviceId, Guid.Empty.ToString("N", CultureInfo.InvariantCulture)),
new Claim(InternalClaimTypes.Device, "test"),
new Claim(InternalClaimTypes.Client, "test"),
new Claim(InternalClaimTypes.Version, "test"),
new Claim(InternalClaimTypes.Token, "test"),
};
var identity = new ClaimsIdentity(claims); var identity = new ClaimsIdentity(claims);
return new ClaimsPrincipal(identity); return (user, new ClaimsPrincipal(identity));
} }
private void SetupConfigurationManager(bool startupWizardCompleted) private void SetupConfigurationManager(bool startupWizardCompleted)

View File

@ -1,20 +1,48 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using AutoFixture;
using AutoFixture.AutoMoq;
using Jellyfin.Api.Auth.RequiresElevationPolicy; using Jellyfin.Api.Auth.RequiresElevationPolicy;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using Jellyfin.Server.Implementations.Users;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Moq;
using Xunit; using Xunit;
namespace Jellyfin.Api.Tests.Auth.RequiresElevationPolicy namespace Jellyfin.Api.Tests.Auth.RequiresElevationPolicy
{ {
public class RequiresElevationHandlerTests public class RequiresElevationHandlerTests
{ {
/// <summary>
/// 127.0.0.1.
/// </summary>
private const long InternalIp = 16777343;
private readonly Mock<IConfigurationManager> _configurationManagerMock;
private readonly List<IAuthorizationRequirement> _requirements;
private readonly RequiresElevationHandler _sut; private readonly RequiresElevationHandler _sut;
private readonly Mock<IUserManager> _userManagerMock;
private readonly Mock<IHttpContextAccessor> _httpContextAccessor;
public RequiresElevationHandlerTests() public RequiresElevationHandlerTests()
{ {
_sut = new RequiresElevationHandler(); var fixture = new Fixture().Customize(new AutoMoqCustomization());
_configurationManagerMock = fixture.Freeze<Mock<IConfigurationManager>>();
_requirements = new List<IAuthorizationRequirement> { new RequiresElevationRequirement() };
_userManagerMock = fixture.Freeze<Mock<IUserManager>>();
_httpContextAccessor = fixture.Freeze<Mock<IHttpContextAccessor>>();
_sut = fixture.Create<RequiresElevationHandler>();
} }
[Theory] [Theory]
@ -23,16 +51,54 @@ namespace Jellyfin.Api.Tests.Auth.RequiresElevationPolicy
[InlineData(UserRoles.Guest, false)] [InlineData(UserRoles.Guest, false)]
public async Task ShouldHandleRolesCorrectly(string role, bool shouldSucceed) public async Task ShouldHandleRolesCorrectly(string role, bool shouldSucceed)
{ {
var requirements = new List<IAuthorizationRequirement> { new RequiresElevationRequirement() }; SetupConfigurationManager(true);
var (user, claims) = SetupUser(role);
var claims = new[] { new Claim(ClaimTypes.Role, role) }; _userManagerMock.Setup(u => u.GetUserById(It.IsAny<Guid>()))
var identity = new ClaimsIdentity(claims); .Returns(user);
var user = new ClaimsPrincipal(identity);
var context = new AuthorizationHandlerContext(requirements, user, null); _httpContextAccessor.Setup(h => h.HttpContext.Connection.RemoteIpAddress)
.Returns(new IPAddress(InternalIp));
var context = new AuthorizationHandlerContext(_requirements, claims, null);
await _sut.HandleAsync(context); await _sut.HandleAsync(context);
Assert.Equal(shouldSucceed, context.HasSucceeded); Assert.Equal(shouldSucceed, context.HasSucceeded);
} }
private static (User, ClaimsPrincipal) SetupUser(string role)
{
var user = new User(
"jellyfin",
typeof(DefaultAuthenticationProvider).FullName,
typeof(DefaultPasswordResetProvider).FullName);
user.SetPermission(PermissionKind.IsAdministrator, role.Equals(UserRoles.Administrator, StringComparison.OrdinalIgnoreCase));
var claims = new[]
{
new Claim(ClaimTypes.Role, role),
new Claim(ClaimTypes.Name, "jellyfin"),
new Claim(InternalClaimTypes.UserId, Guid.Empty.ToString("N", CultureInfo.InvariantCulture)),
new Claim(InternalClaimTypes.DeviceId, Guid.Empty.ToString("N", CultureInfo.InvariantCulture)),
new Claim(InternalClaimTypes.Device, "test"),
new Claim(InternalClaimTypes.Client, "test"),
new Claim(InternalClaimTypes.Version, "test"),
new Claim(InternalClaimTypes.Token, "test"),
};
var identity = new ClaimsIdentity(claims);
return (user, new ClaimsPrincipal(identity));
}
private void SetupConfigurationManager(bool startupWizardCompleted)
{
var commonConfiguration = new BaseApplicationConfiguration
{
IsStartupWizardCompleted = startupWizardCompleted
};
_configurationManagerMock.Setup(c => c.CommonConfiguration)
.Returns(commonConfiguration);
}
} }
} }

View File

@ -35,6 +35,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="../../MediaBrowser.Api/MediaBrowser.Api.csproj" /> <ProjectReference Include="../../MediaBrowser.Api/MediaBrowser.Api.csproj" />
<ProjectReference Include="../../Jellyfin.Api/Jellyfin.Api.csproj" /> <ProjectReference Include="../../Jellyfin.Api/Jellyfin.Api.csproj" />
<ProjectReference Include="..\..\Jellyfin.Server\Jellyfin.Server.csproj" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">