diff --git a/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs
index e26214371c..c5c87056df 100644
--- a/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs
+++ b/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs
@@ -25,19 +25,30 @@ namespace Jellyfin.Api.Auth.FirstTimeSetupPolicy
///
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupRequirement requirement)
{
+ // Succeed if the startup wizard / first time setup is not complete
if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted)
{
context.Succeed(requirement);
}
- else if (context.User.IsInRole(UserRoles.Administrator))
+
+ // Succeed if user is admin or api key
+ else if (context.User.GetIsApiKey() || context.User.IsInRole(UserRoles.Administrator))
{
context.Succeed(requirement);
}
+
+ // Fail if admin is required and user is not admin
else if (requirement.RequireAdmin && !context.User.IsInRole(UserRoles.Administrator))
{
context.Fail();
}
+ // Succeed if admin is not required and user is not guest
+ else if (!requirement.RequireAdmin && context.User.IsInRole(UserRoles.User))
+ {
+ context.Succeed(requirement);
+ }
+
// Any user-specific checks are handled in the DefaultAuthorizationHandler.
return Task.CompletedTask;
}
diff --git a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs
index c2bbc6ba0c..35a24a1291 100644
--- a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs
@@ -1,14 +1,19 @@
+using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using AutoFixture;
using AutoFixture.AutoMoq;
+using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
using Jellyfin.Api.Auth.FirstTimeSetupPolicy;
using Jellyfin.Api.Constants;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
using Moq;
using Xunit;
@@ -18,7 +23,9 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupPolicy
{
private readonly Mock _configurationManagerMock;
private readonly List _requirements;
+ private readonly DefaultAuthorizationHandler _defaultAuthorizationHandler;
private readonly FirstTimeSetupHandler _firstTimeSetupHandler;
+ private readonly IAuthorizationService _authorizationService;
private readonly Mock _userManagerMock;
private readonly Mock _httpContextAccessor;
@@ -31,6 +38,21 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupPolicy
_httpContextAccessor = fixture.Freeze>();
_firstTimeSetupHandler = fixture.Create();
+ _defaultAuthorizationHandler = fixture.Create();
+
+ var services = new ServiceCollection();
+ services.AddAuthorizationCore();
+ services.AddLogging();
+ services.AddOptions();
+ services.AddSingleton(_defaultAuthorizationHandler);
+ services.AddSingleton(_firstTimeSetupHandler);
+ services.AddAuthorization(options =>
+ {
+ options.AddPolicy("FirstTime", policy => policy.Requirements.Add(new FirstTimeSetupRequirement()));
+ options.AddPolicy("FirstTimeNoAdmin", policy => policy.Requirements.Add(new FirstTimeSetupRequirement(false, false)));
+ options.AddPolicy("FirstTimeSchedule", policy => policy.Requirements.Add(new FirstTimeSetupRequirement(true, false)));
+ });
+ _authorizationService = services.BuildServiceProvider().GetRequiredService();
}
[Theory]
@@ -45,17 +67,33 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupPolicy
_httpContextAccessor,
userRole);
- var context = new AuthorizationHandlerContext(_requirements, claims, null);
+ var allowed = await _authorizationService.AuthorizeAsync(claims, "FirstTime");
- await _firstTimeSetupHandler.HandleAsync(context);
- Assert.True(context.HasSucceeded);
+ Assert.True(allowed.Succeeded);
}
[Theory]
- [InlineData(UserRoles.Administrator, false)]
- [InlineData(UserRoles.Guest, true)]
+ [InlineData(UserRoles.Administrator, true)]
+ [InlineData(UserRoles.Guest, false)]
+ [InlineData(UserRoles.User, false)]
+ public async Task ShouldRequireAdministratorIfStartupWizardComplete(string userRole, bool shouldSucceed)
+ {
+ TestHelpers.SetupConfigurationManager(_configurationManagerMock, true);
+ var claims = TestHelpers.SetupUser(
+ _userManagerMock,
+ _httpContextAccessor,
+ userRole);
+
+ var allowed = await _authorizationService.AuthorizeAsync(claims, "FirstTime");
+
+ Assert.Equal(shouldSucceed, allowed.Succeeded);
+ }
+
+ [Theory]
+ [InlineData(UserRoles.Administrator, true)]
+ [InlineData(UserRoles.Guest, false)]
[InlineData(UserRoles.User, true)]
- public async Task ShouldRequireAdministratorIfStartupWizardComplete(string userRole, bool shouldFail)
+ public async Task ShouldRequireUserIfNotAdministrator(string userRole, bool shouldSucceed)
{
TestHelpers.SetupConfigurationManager(_configurationManagerMock, true);
var claims = TestHelpers.SetupUser(
@@ -63,32 +101,9 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupPolicy
_httpContextAccessor,
userRole);
- var context = new AuthorizationHandlerContext(_requirements, claims, null);
+ var allowed = await _authorizationService.AuthorizeAsync(claims, "FirstTimeNoAdmin");
- await _firstTimeSetupHandler.HandleAsync(context);
- Assert.Equal(shouldFail, context.HasFailed);
- }
-
- [Theory]
- [InlineData(UserRoles.Administrator)]
- [InlineData(UserRoles.Guest)]
- [InlineData(UserRoles.User)]
- public async Task ShouldDeferIfNotRequiresAdmin(string userRole)
- {
- TestHelpers.SetupConfigurationManager(_configurationManagerMock, true);
- var claims = TestHelpers.SetupUser(
- _userManagerMock,
- _httpContextAccessor,
- userRole);
-
- var context = new AuthorizationHandlerContext(
- new List { new FirstTimeSetupRequirement(false, false) },
- claims,
- null);
-
- await _firstTimeSetupHandler.HandleAsync(context);
- Assert.False(context.HasSucceeded);
- Assert.False(context.HasFailed);
+ Assert.Equal(shouldSucceed, allowed.Succeeded);
}
[Fact]
@@ -96,10 +111,26 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupPolicy
{
TestHelpers.SetupConfigurationManager(_configurationManagerMock, true);
var claims = new ClaimsPrincipal(new ClaimsIdentity([new Claim(InternalClaimTypes.IsApiKey, bool.TrueString)]));
- var context = new AuthorizationHandlerContext(_requirements, claims, null);
- await _firstTimeSetupHandler.HandleAsync(context);
- Assert.True(context.HasSucceeded);
+ var allowed = await _authorizationService.AuthorizeAsync(claims, "FirstTime");
+ Assert.True(allowed.Succeeded);
+ }
+
+ [Fact]
+ public async Task ShouldDisallowUserIfOutsideSchedule()
+ {
+ AccessSchedule[] accessSchedules = { new AccessSchedule(DynamicDayOfWeek.Everyday, 0, 0, Guid.Empty) };
+
+ TestHelpers.SetupConfigurationManager(_configurationManagerMock, true);
+ var claims = TestHelpers.SetupUser(
+ _userManagerMock,
+ _httpContextAccessor,
+ UserRoles.User,
+ accessSchedules);
+
+ var allowed = await _authorizationService.AuthorizeAsync(claims, "FirstTimeSchedule");
+
+ Assert.False(allowed.Succeeded);
}
}
}