2
0
crobibero 5 жил өмнө
parent
commit
774fdbd031

+ 1 - 1
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);

+ 1 - 0
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);

+ 21 - 50
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()

+ 59 - 7
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)

+ 72 - 6
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);
+        }
     }
 }

+ 1 - 0
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' ">