aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jellyfin.Api/Auth/CustomAuthenticationHandler.cs2
-rw-r--r--Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs1
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs71
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs66
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs78
-rw-r--r--tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj1
6 files changed, 155 insertions, 64 deletions
diff --git a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
index d4d40da57..5e5e25e84 100644
--- a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
+++ b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
@@ -62,7 +62,7 @@ namespace Jellyfin.Api.Auth
new Claim(InternalClaimTypes.Device, authorizationInfo.Device),
new Claim(InternalClaimTypes.Client, authorizationInfo.Client),
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);
diff --git a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs
index 0b12f7d3c..decbe0c03 100644
--- a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs
+++ b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs
@@ -38,6 +38,7 @@ namespace Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy
if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted)
{
context.Succeed(firstTimeSetupOrElevatedRequirement);
+ return Task.CompletedTask;
}
var validated = ValidateClaims(context.User);
diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
index 362d41b01..4ea5094b6 100644
--- a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
@@ -1,7 +1,6 @@
using System;
using System.Linq;
using System.Security.Claims;
-using System.Text.Encodings.Web;
using System.Threading.Tasks;
using AutoFixture;
using AutoFixture.AutoMoq;
@@ -9,7 +8,6 @@ using Jellyfin.Api.Auth;
using Jellyfin.Api.Constants;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Net;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
@@ -26,12 +24,6 @@ namespace Jellyfin.Api.Tests.Auth
private readonly IFixture _fixture;
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 AuthenticationScheme _scheme;
@@ -47,26 +39,23 @@ namespace Jellyfin.Api.Tests.Auth
AllowFixtureCircularDependencies();
_jellyfinAuthServiceMock = _fixture.Freeze<Mock<IAuthService>>();
- _optionsMonitorMock = _fixture.Freeze<Mock<IOptionsMonitor<AuthenticationSchemeOptions>>>();
- _clockMock = _fixture.Freeze<Mock<ISystemClock>>();
- _serviceProviderMock = _fixture.Freeze<Mock<IServiceProvider>>();
- _authenticationServiceMock = _fixture.Freeze<Mock<IAuthenticationService>>();
+ var optionsMonitorMock = _fixture.Freeze<Mock<IOptionsMonitor<AuthenticationSchemeOptions>>>();
+ var serviceProviderMock = _fixture.Freeze<Mock<IServiceProvider>>();
+ var authenticationServiceMock = _fixture.Freeze<Mock<IAuthenticationService>>();
_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)))
- .Returns(_authenticationServiceMock.Object);
-
- _optionsMonitorMock.Setup(o => o.Get(It.IsAny<string>()))
+ optionsMonitorMock.Setup(o => o.Get(It.IsAny<string>()))
.Returns(new AuthenticationSchemeOptions
{
ForwardAuthenticate = null
});
- _context = new DefaultHttpContext
+ HttpContext context = new DefaultHttpContext
{
- RequestServices = _serviceProviderMock.Object
+ RequestServices = serviceProviderMock.Object
};
_scheme = new AuthenticationScheme(
@@ -75,24 +64,7 @@ namespace Jellyfin.Api.Tests.Auth
typeof(CustomAuthenticationHandler));
_sut = _fixture.Create<CustomAuthenticationHandler>();
- _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);
+ _sut.InitializeAsync(_scheme, context).Wait();
}
[Fact]
@@ -102,8 +74,7 @@ namespace Jellyfin.Api.Tests.Auth
_jellyfinAuthServiceMock.Setup(
a => a.Authenticate(
- It.IsAny<HttpRequest>(),
- It.IsAny<AuthenticatedAttribute>()))
+ It.IsAny<HttpRequest>()))
.Throws(new SecurityException(errorMessage));
var authenticateResult = await _sut.AuthenticateAsync();
@@ -125,10 +96,10 @@ namespace Jellyfin.Api.Tests.Auth
[Fact]
public async Task HandleAuthenticateAsyncShouldAssignNameClaim()
{
- var user = SetupUser();
+ var authorizationInfo = SetupUser();
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]
@@ -136,10 +107,10 @@ namespace Jellyfin.Api.Tests.Auth
[InlineData(false)]
public async Task HandleAuthenticateAsyncShouldAssignRoleClaim(bool isAdmin)
{
- var user = SetupUser(isAdmin);
+ var authorizationInfo = SetupUser(isAdmin);
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));
}
@@ -152,18 +123,18 @@ namespace Jellyfin.Api.Tests.Auth
Assert.Equal(_scheme.Name, authenticatedResult.Ticket.AuthenticationScheme);
}
- private User SetupUser(bool isAdmin = false)
+ private AuthorizationInfo SetupUser(bool isAdmin = false)
{
- var user = _fixture.Create<User>();
- user.SetPermission(PermissionKind.IsAdministrator, isAdmin);
+ var authorizationInfo = _fixture.Create<AuthorizationInfo>();
+ authorizationInfo.User = _fixture.Create<User>();
+ authorizationInfo.User.SetPermission(PermissionKind.IsAdministrator, isAdmin);
_jellyfinAuthServiceMock.Setup(
a => a.Authenticate(
- It.IsAny<HttpRequest>(),
- It.IsAny<AuthenticatedAttribute>()))
- .Returns(user);
+ It.IsAny<HttpRequest>()))
+ .Returns(authorizationInfo);
- return user;
+ return authorizationInfo;
}
private void AllowFixtureCircularDependencies()
diff --git a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs
index e40af703f..e455df643 100644
--- a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs
@@ -1,13 +1,21 @@
+using System;
using System.Collections.Generic;
+using System.Globalization;
+using System.Net;
using System.Security.Claims;
using System.Threading.Tasks;
using AutoFixture;
using AutoFixture.AutoMoq;
using Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy;
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.Http;
using Moq;
using Xunit;
@@ -15,15 +23,28 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy
{
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 List<IAuthorizationRequirement> _requirements;
private readonly FirstTimeSetupOrElevatedHandler _sut;
+ private readonly Mock<IUserManager> _userManagerMock;
+ private readonly Mock<IHttpContextAccessor> _httpContextAccessor;
public FirstTimeSetupOrElevatedHandlerTests()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());
_configurationManagerMock = fixture.Freeze<Mock<IConfigurationManager>>();
_requirements = new List<IAuthorizationRequirement> { new FirstTimeSetupOrElevatedRequirement() };
+ _userManagerMock = fixture.Freeze<Mock<IUserManager>>();
+ _httpContextAccessor = fixture.Freeze<Mock<IHttpContextAccessor>>();
_sut = fixture.Create<FirstTimeSetupOrElevatedHandler>();
}
@@ -35,8 +56,15 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy
public async Task ShouldSucceedIfStartupWizardIncomplete(string userRole)
{
SetupConfigurationManager(false);
- var user = SetupUser(userRole);
- var context = new AuthorizationHandlerContext(_requirements, user, null);
+ var (user, claims) = SetupUser(userRole);
+
+ _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);
Assert.True(context.HasSucceeded);
@@ -49,18 +77,42 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy
public async Task ShouldRequireAdministratorIfStartupWizardComplete(string userRole, bool shouldSucceed)
{
SetupConfigurationManager(true);
- var user = SetupUser(userRole);
- var context = new AuthorizationHandlerContext(_requirements, user, null);
+ var (user, claims) = SetupUser(userRole);
+
+ _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);
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);
- return new ClaimsPrincipal(identity);
+ return (user, new ClaimsPrincipal(identity));
}
private void SetupConfigurationManager(bool startupWizardCompleted)
diff --git a/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs
index cd05a8328..58eae8478 100644
--- a/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs
@@ -1,20 +1,48 @@
+using System;
using System.Collections.Generic;
+using System.Globalization;
+using System.Net;
using System.Security.Claims;
using System.Threading.Tasks;
+using AutoFixture;
+using AutoFixture.AutoMoq;
using Jellyfin.Api.Auth.RequiresElevationPolicy;
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.Http;
+using Moq;
using Xunit;
namespace Jellyfin.Api.Tests.Auth.RequiresElevationPolicy
{
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 Mock<IUserManager> _userManagerMock;
+ private readonly Mock<IHttpContextAccessor> _httpContextAccessor;
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]
@@ -23,16 +51,54 @@ namespace Jellyfin.Api.Tests.Auth.RequiresElevationPolicy
[InlineData(UserRoles.Guest, false)]
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) };
- var identity = new ClaimsIdentity(claims);
- var user = new ClaimsPrincipal(identity);
+ _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, user, null);
+ var context = new AuthorizationHandlerContext(_requirements, claims, null);
await _sut.HandleAsync(context);
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);
+ }
}
}
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index aedcc7c42..010fad520 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -35,6 +35,7 @@
<ItemGroup>
<ProjectReference Include="../../MediaBrowser.Api/MediaBrowser.Api.csproj" />
<ProjectReference Include="../../Jellyfin.Api/Jellyfin.Api.csproj" />
+ <ProjectReference Include="..\..\Jellyfin.Server\Jellyfin.Server.csproj" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">