2
0
cvium 2 жил өмнө
parent
commit
209edd38a4
75 өөрчлөгдсөн 393 нэмэгдсэн , 1025 устгасан
  1. 2 1
      Jellyfin.Api/Auth/AnonymousLanAccessPolicy/AnonymousLanAccessHandler.cs
  2. 0 113
      Jellyfin.Api/Auth/BaseAuthorizationHandler.cs
  3. 42 5
      Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs
  4. 13 0
      Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationRequirement.cs
  5. 0 44
      Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs
  6. 0 11
      Jellyfin.Api/Auth/DownloadPolicy/DownloadRequirement.cs
  7. 0 56
      Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs
  8. 0 11
      Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupRequirement.cs
  9. 0 11
      Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultRequirement.cs
  10. 0 57
      Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs
  11. 0 11
      Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedRequirement.cs
  12. 22 17
      Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs
  13. 26 0
      Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupRequirement.cs
  14. 0 44
      Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs
  15. 0 11
      Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlRequirement.cs
  16. 0 45
      Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs
  17. 0 11
      Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationRequirement.cs
  18. 0 44
      Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs
  19. 0 11
      Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessRequirement.cs
  20. 0 45
      Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs
  21. 0 11
      Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationRequirement.cs
  22. 3 38
      Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs
  23. 3 3
      Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs
  24. 37 0
      Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs
  25. 26 0
      Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs
  26. 0 5
      Jellyfin.Api/Constants/Policies.cs
  27. 1 2
      Jellyfin.Api/Controllers/ArtistsController.cs
  28. 1 2
      Jellyfin.Api/Controllers/ChannelsController.cs
  29. 1 2
      Jellyfin.Api/Controllers/ClientLogController.cs
  30. 1 2
      Jellyfin.Api/Controllers/CollectionController.cs
  31. 1 1
      Jellyfin.Api/Controllers/ConfigurationController.cs
  32. 1 2
      Jellyfin.Api/Controllers/DashboardController.cs
  33. 1 2
      Jellyfin.Api/Controllers/DisplayPreferencesController.cs
  34. 1 2
      Jellyfin.Api/Controllers/DynamicHlsController.cs
  35. 1 2
      Jellyfin.Api/Controllers/FilterController.cs
  36. 2 3
      Jellyfin.Api/Controllers/GenresController.cs
  37. 2 3
      Jellyfin.Api/Controllers/HlsSegmentController.cs
  38. 9 9
      Jellyfin.Api/Controllers/ImageController.cs
  39. 1 2
      Jellyfin.Api/Controllers/InstantMixController.cs
  40. 1 1
      Jellyfin.Api/Controllers/ItemLookupController.cs
  41. 1 2
      Jellyfin.Api/Controllers/ItemsController.cs
  42. 40 40
      Jellyfin.Api/Controllers/LibraryController.cs
  43. 38 39
      Jellyfin.Api/Controllers/LiveTvController.cs
  44. 1 2
      Jellyfin.Api/Controllers/MediaInfoController.cs
  45. 1 2
      Jellyfin.Api/Controllers/MoviesController.cs
  46. 1 2
      Jellyfin.Api/Controllers/MusicGenresController.cs
  47. 1 1
      Jellyfin.Api/Controllers/PackageController.cs
  48. 1 2
      Jellyfin.Api/Controllers/PersonsController.cs
  49. 1 2
      Jellyfin.Api/Controllers/PlaylistsController.cs
  50. 1 2
      Jellyfin.Api/Controllers/PlaystateController.cs
  51. 1 1
      Jellyfin.Api/Controllers/PluginsController.cs
  52. 1 1
      Jellyfin.Api/Controllers/QuickConnectController.cs
  53. 2 2
      Jellyfin.Api/Controllers/RemoteImageController.cs
  54. 1 2
      Jellyfin.Api/Controllers/SearchController.cs
  55. 14 14
      Jellyfin.Api/Controllers/SessionController.cs
  56. 1 2
      Jellyfin.Api/Controllers/StudiosController.cs
  57. 6 6
      Jellyfin.Api/Controllers/SubtitleController.cs
  58. 1 2
      Jellyfin.Api/Controllers/SuggestionsController.cs
  59. 2 2
      Jellyfin.Api/Controllers/SystemController.cs
  60. 1 2
      Jellyfin.Api/Controllers/TrailersController.cs
  61. 1 2
      Jellyfin.Api/Controllers/TvShowsController.cs
  62. 1 2
      Jellyfin.Api/Controllers/UniversalAudioController.cs
  63. 6 6
      Jellyfin.Api/Controllers/UserController.cs
  64. 1 2
      Jellyfin.Api/Controllers/UserLibraryController.cs
  65. 1 2
      Jellyfin.Api/Controllers/UserViewsController.cs
  66. 1 1
      Jellyfin.Api/Controllers/VideosController.cs
  67. 1 2
      Jellyfin.Api/Controllers/YearsController.cs
  68. 8 3
      Jellyfin.Api/Middleware/LanFilteringMiddleware.cs
  69. 11 0
      Jellyfin.Data/DayOfWeekHelper.cs
  70. 2 1
      Jellyfin.Data/Entities/User.cs
  71. 32 111
      Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
  72. 10 9
      tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs
  73. 4 4
      tests/Jellyfin.Api.Tests/Auth/IgnoreSchedulePolicy/IgnoreScheduleHandlerTests.cs
  74. 0 59
      tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs
  75. 0 53
      tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs

+ 2 - 1
Jellyfin.Api/Auth/AnonymousLanAccessPolicy/AnonymousLanAccessHandler.cs

@@ -1,4 +1,5 @@
 using System.Threading.Tasks;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
@@ -29,7 +30,7 @@ namespace Jellyfin.Api.Auth.AnonymousLanAccessPolicy
         /// <inheritdoc />
         protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AnonymousLanAccessRequirement requirement)
         {
-            var ip = _httpContextAccessor.HttpContext?.Connection.RemoteIpAddress;
+            var ip = _httpContextAccessor.HttpContext?.GetNormalizedRemoteIp();
 
             // Loopback will be on LAN, so we can accept null.
             if (ip is null || _networkManager.IsInLocalNetwork(ip))

+ 0 - 113
Jellyfin.Api/Auth/BaseAuthorizationHandler.cs

@@ -1,113 +0,0 @@
-using System.Security.Claims;
-using Jellyfin.Api.Extensions;
-using Jellyfin.Api.Helpers;
-using Jellyfin.Data.Enums;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Library;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-
-namespace Jellyfin.Api.Auth
-{
-    /// <summary>
-    /// Base authorization handler.
-    /// </summary>
-    /// <typeparam name="T">Type of Authorization Requirement.</typeparam>
-    public abstract class BaseAuthorizationHandler<T> : AuthorizationHandler<T>
-        where T : IAuthorizationRequirement
-    {
-        private readonly IUserManager _userManager;
-        private readonly INetworkManager _networkManager;
-        private readonly IHttpContextAccessor _httpContextAccessor;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="BaseAuthorizationHandler{T}"/> class.
-        /// </summary>
-        /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
-        /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
-        /// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
-        protected BaseAuthorizationHandler(
-            IUserManager userManager,
-            INetworkManager networkManager,
-            IHttpContextAccessor httpContextAccessor)
-        {
-            _userManager = userManager;
-            _networkManager = networkManager;
-            _httpContextAccessor = httpContextAccessor;
-        }
-
-        /// <summary>
-        /// Validate authenticated claims.
-        /// </summary>
-        /// <param name="claimsPrincipal">Request claims.</param>
-        /// <param name="ignoreSchedule">Whether to ignore parental control.</param>
-        /// <param name="localAccessOnly">Whether access is to be allowed locally only.</param>
-        /// <param name="requiredDownloadPermission">Whether validation requires download permission.</param>
-        /// <returns>Validated claim status.</returns>
-        protected bool ValidateClaims(
-            ClaimsPrincipal claimsPrincipal,
-            bool ignoreSchedule = false,
-            bool localAccessOnly = false,
-            bool requiredDownloadPermission = false)
-        {
-            // ApiKey is currently global admin, always allow.
-            var isApiKey = claimsPrincipal.GetIsApiKey();
-            if (isApiKey)
-            {
-                return true;
-            }
-
-            // Ensure claim has userId.
-            var userId = claimsPrincipal.GetUserId();
-            if (userId.Equals(default))
-            {
-                return false;
-            }
-
-            // Ensure userId links to a valid user.
-            var user = _userManager.GetUserById(userId);
-            if (user is null)
-            {
-                return false;
-            }
-
-            // Ensure user is not disabled.
-            if (user.HasPermission(PermissionKind.IsDisabled))
-            {
-                return false;
-            }
-
-            var isInLocalNetwork = _httpContextAccessor.HttpContext is not null
-                && _networkManager.IsInLocalNetwork(_httpContextAccessor.HttpContext.GetNormalizedRemoteIp());
-
-            // User cannot access remotely and user is remote
-            if (!user.HasPermission(PermissionKind.EnableRemoteAccess) && !isInLocalNetwork)
-            {
-                return false;
-            }
-
-            if (localAccessOnly && !isInLocalNetwork)
-            {
-                return false;
-            }
-
-            // User attempting to access out of parental control hours.
-            if (!ignoreSchedule
-                && !user.HasPermission(PermissionKind.IsAdministrator)
-                && !user.IsParentalScheduleAllowed())
-            {
-                return false;
-            }
-
-            // User attempting to download without permission.
-            if (requiredDownloadPermission
-                && !user.HasPermission(PermissionKind.EnableContentDownloading))
-            {
-                return false;
-            }
-
-            return true;
-        }
-    }
-}

+ 42 - 5
Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs

@@ -1,4 +1,8 @@
 using System.Threading.Tasks;
+using Jellyfin.Api.Constants;
+using Jellyfin.Api.Extensions;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Library;
 using Microsoft.AspNetCore.Authorization;
@@ -9,8 +13,12 @@ namespace Jellyfin.Api.Auth.DefaultAuthorizationPolicy
     /// <summary>
     /// Default authorization handler.
     /// </summary>
-    public class DefaultAuthorizationHandler : BaseAuthorizationHandler<DefaultAuthorizationRequirement>
+    public class DefaultAuthorizationHandler : AuthorizationHandler<DefaultAuthorizationRequirement>
     {
+        private readonly IUserManager _userManager;
+        private readonly INetworkManager _networkManager;
+        private readonly IHttpContextAccessor _httpContextAccessor;
+
         /// <summary>
         /// Initializes a new instance of the <see cref="DefaultAuthorizationHandler"/> class.
         /// </summary>
@@ -21,21 +29,50 @@ namespace Jellyfin.Api.Auth.DefaultAuthorizationPolicy
             IUserManager userManager,
             INetworkManager networkManager,
             IHttpContextAccessor httpContextAccessor)
-            : base(userManager, networkManager, httpContextAccessor)
         {
+            _userManager = userManager;
+            _networkManager = networkManager;
+            _httpContextAccessor = httpContextAccessor;
         }
 
         /// <inheritdoc />
         protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DefaultAuthorizationRequirement requirement)
         {
-            var validated = ValidateClaims(context.User);
-            if (validated)
+            // Admins can do everything
+            if (context.User.GetIsApiKey() || context.User.IsInRole(UserRoles.Administrator))
             {
                 context.Succeed(requirement);
+                return Task.CompletedTask;
             }
-            else
+
+            var userId = context.User.GetUserId();
+            // This likely only happens during the wizard, so skip the default checks and let any other handlers do it
+            if (userId.Equals(default))
+            {
+                return Task.CompletedTask;
+            }
+
+            var isInLocalNetwork = _httpContextAccessor.HttpContext is not null
+                                   && _networkManager.IsInLocalNetwork(_httpContextAccessor.HttpContext.GetNormalizedRemoteIp());
+            var user = _userManager.GetUserById(userId);
+            // User cannot access remotely and user is remote
+            if (!isInLocalNetwork && !user.HasPermission(PermissionKind.EnableRemoteAccess))
             {
                 context.Fail();
+                return Task.CompletedTask;
+            }
+
+            // It's not great to have this check, but parental schedule must usually be honored except in a few rare cases
+            if (requirement.ValidateParentalSchedule && !user.IsParentalScheduleAllowed())
+            {
+                context.Fail();
+                return Task.CompletedTask;
+            }
+
+            // Only succeed if the requirement isn't a subclass as any subclassed requirement will handle success in its own handler
+            if (requirement.GetType() == typeof(DefaultAuthorizationRequirement))
+            {
+                context.Succeed(requirement);
             }
 
             return Task.CompletedTask;

+ 13 - 0
Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationRequirement.cs

@@ -7,5 +7,18 @@ namespace Jellyfin.Api.Auth.DefaultAuthorizationPolicy
     /// </summary>
     public class DefaultAuthorizationRequirement : IAuthorizationRequirement
     {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="DefaultAuthorizationRequirement"/> class.
+        /// </summary>
+        /// <param name="validateParentalSchedule">A value indicating whether to validate parental schedule.</param>
+        public DefaultAuthorizationRequirement(bool validateParentalSchedule = true)
+        {
+            ValidateParentalSchedule = validateParentalSchedule;
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether to ignore parental schedule.
+        /// </summary>
+        public bool ValidateParentalSchedule { get; init; }
     }
 }

+ 0 - 44
Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs

@@ -1,44 +0,0 @@
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Library;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-
-namespace Jellyfin.Api.Auth.DownloadPolicy
-{
-    /// <summary>
-    /// Download authorization handler.
-    /// </summary>
-    public class DownloadHandler : BaseAuthorizationHandler<DownloadRequirement>
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="DownloadHandler"/> class.
-        /// </summary>
-        /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
-        /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
-        /// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
-        public DownloadHandler(
-            IUserManager userManager,
-            INetworkManager networkManager,
-            IHttpContextAccessor httpContextAccessor)
-            : base(userManager, networkManager, httpContextAccessor)
-        {
-        }
-
-        /// <inheritdoc />
-        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DownloadRequirement requirement)
-        {
-            var validated = ValidateClaims(context.User);
-            if (validated)
-            {
-                context.Succeed(requirement);
-            }
-            else
-            {
-                context.Fail();
-            }
-
-            return Task.CompletedTask;
-        }
-    }
-}

+ 0 - 11
Jellyfin.Api/Auth/DownloadPolicy/DownloadRequirement.cs

@@ -1,11 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-
-namespace Jellyfin.Api.Auth.DownloadPolicy
-{
-    /// <summary>
-    /// The download permission requirement.
-    /// </summary>
-    public class DownloadRequirement : IAuthorizationRequirement
-    {
-    }
-}

+ 0 - 56
Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs

@@ -1,56 +0,0 @@
-using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Library;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-
-namespace Jellyfin.Api.Auth.FirstTimeOrIgnoreParentalControlSetupPolicy
-{
-    /// <summary>
-    /// Ignore parental control schedule and allow before startup wizard has been completed.
-    /// </summary>
-    public class FirstTimeOrIgnoreParentalControlSetupHandler : BaseAuthorizationHandler<FirstTimeOrIgnoreParentalControlSetupRequirement>
-    {
-        private readonly IConfigurationManager _configurationManager;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="FirstTimeOrIgnoreParentalControlSetupHandler"/> class.
-        /// </summary>
-        /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
-        /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
-        /// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
-        /// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
-        public FirstTimeOrIgnoreParentalControlSetupHandler(
-            IUserManager userManager,
-            INetworkManager networkManager,
-            IHttpContextAccessor httpContextAccessor,
-            IConfigurationManager configurationManager)
-            : base(userManager, networkManager, httpContextAccessor)
-        {
-            _configurationManager = configurationManager;
-        }
-
-        /// <inheritdoc />
-        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeOrIgnoreParentalControlSetupRequirement requirement)
-        {
-            if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted)
-            {
-                context.Succeed(requirement);
-                return Task.CompletedTask;
-            }
-
-            var validated = ValidateClaims(context.User, ignoreSchedule: true);
-            if (validated)
-            {
-                context.Succeed(requirement);
-            }
-            else
-            {
-                context.Fail();
-            }
-
-            return Task.CompletedTask;
-        }
-    }
-}

+ 0 - 11
Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupRequirement.cs

@@ -1,11 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-
-namespace Jellyfin.Api.Auth.FirstTimeOrIgnoreParentalControlSetupPolicy
-{
-    /// <summary>
-    /// First time setup or ignore parental controls requirement.
-    /// </summary>
-    public class FirstTimeOrIgnoreParentalControlSetupRequirement : IAuthorizationRequirement
-    {
-    }
-}

+ 0 - 11
Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultRequirement.cs

@@ -1,11 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-
-namespace Jellyfin.Api.Auth.FirstTimeSetupOrDefaultPolicy
-{
-    /// <summary>
-    /// The authorization requirement, requiring incomplete first time setup or default privileges, for the authorization handler.
-    /// </summary>
-    public class FirstTimeSetupOrDefaultRequirement : IAuthorizationRequirement
-    {
-    }
-}

+ 0 - 57
Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs

@@ -1,57 +0,0 @@
-using System.Threading.Tasks;
-using Jellyfin.Api.Constants;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Library;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-
-namespace Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy
-{
-    /// <summary>
-    /// Authorization handler for requiring first time setup or elevated privileges.
-    /// </summary>
-    public class FirstTimeSetupOrElevatedHandler : BaseAuthorizationHandler<FirstTimeSetupOrElevatedRequirement>
-    {
-        private readonly IConfigurationManager _configurationManager;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="FirstTimeSetupOrElevatedHandler" /> class.
-        /// </summary>
-        /// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
-        /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
-        /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
-        /// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
-        public FirstTimeSetupOrElevatedHandler(
-            IConfigurationManager configurationManager,
-            IUserManager userManager,
-            INetworkManager networkManager,
-            IHttpContextAccessor httpContextAccessor)
-            : base(userManager, networkManager, httpContextAccessor)
-        {
-            _configurationManager = configurationManager;
-        }
-
-        /// <inheritdoc />
-        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrElevatedRequirement requirement)
-        {
-            if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted)
-            {
-                context.Succeed(requirement);
-                return Task.CompletedTask;
-            }
-
-            var validated = ValidateClaims(context.User);
-            if (validated && context.User.IsInRole(UserRoles.Administrator))
-            {
-                context.Succeed(requirement);
-            }
-            else
-            {
-                context.Fail();
-            }
-
-            return Task.CompletedTask;
-        }
-    }
-}

+ 0 - 11
Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedRequirement.cs

@@ -1,11 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-
-namespace Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy
-{
-    /// <summary>
-    /// The authorization requirement, requiring incomplete first time setup or elevated privileges, for the authorization handler.
-    /// </summary>
-    public class FirstTimeSetupOrElevatedRequirement : IAuthorizationRequirement
-    {
-    }
-}

+ 22 - 17
Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs → Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs

@@ -1,38 +1,35 @@
 using System.Threading.Tasks;
+using Jellyfin.Api.Constants;
+using Jellyfin.Api.Extensions;
 using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Library;
 using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
 
-namespace Jellyfin.Api.Auth.FirstTimeSetupOrDefaultPolicy
+namespace Jellyfin.Api.Auth.FirstTimeSetupPolicy
 {
     /// <summary>
     /// Authorization handler for requiring first time setup or default privileges.
     /// </summary>
-    public class FirstTimeSetupOrDefaultHandler : BaseAuthorizationHandler<FirstTimeSetupOrDefaultRequirement>
+    public class FirstTimeSetupHandler : AuthorizationHandler<FirstTimeSetupRequirement>
     {
         private readonly IConfigurationManager _configurationManager;
+        private readonly IUserManager _userManager;
 
         /// <summary>
-        /// Initializes a new instance of the <see cref="FirstTimeSetupOrDefaultHandler" /> class.
+        /// Initializes a new instance of the <see cref="FirstTimeSetupHandler" /> class.
         /// </summary>
         /// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
         /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
-        /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
-        /// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
-        public FirstTimeSetupOrDefaultHandler(
+        public FirstTimeSetupHandler(
             IConfigurationManager configurationManager,
-            IUserManager userManager,
-            INetworkManager networkManager,
-            IHttpContextAccessor httpContextAccessor)
-            : base(userManager, networkManager, httpContextAccessor)
+            IUserManager userManager)
         {
             _configurationManager = configurationManager;
+            _userManager = userManager;
         }
 
         /// <inheritdoc />
-        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrDefaultRequirement requirement)
+        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupRequirement requirement)
         {
             if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted)
             {
@@ -40,14 +37,22 @@ namespace Jellyfin.Api.Auth.FirstTimeSetupOrDefaultPolicy
                 return Task.CompletedTask;
             }
 
-            var validated = ValidateClaims(context.User);
-            if (validated)
+            if (requirement.RequireAdmin && !context.User.IsInRole(UserRoles.Administrator))
+            {
+                context.Fail();
+                return Task.CompletedTask;
+            }
+
+            if (!requirement.ValidateParentalSchedule)
             {
                 context.Succeed(requirement);
+                return Task.CompletedTask;
             }
-            else
+
+            var user = _userManager.GetUserById(context.User.GetUserId());
+            if (user.IsParentalScheduleAllowed())
             {
-                context.Fail();
+                context.Succeed(requirement);
             }
 
             return Task.CompletedTask;

+ 26 - 0
Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupRequirement.cs

@@ -0,0 +1,26 @@
+using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
+
+namespace Jellyfin.Api.Auth.FirstTimeSetupPolicy
+{
+    /// <summary>
+    /// The authorization requirement, requiring incomplete first time setup or default privileges, for the authorization handler.
+    /// </summary>
+    public class FirstTimeSetupRequirement : DefaultAuthorizationRequirement
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="FirstTimeSetupRequirement"/> class.
+        /// </summary>
+        /// <param name="validateParentalSchedule">A value indicating whether to ignore parental schedule.</param>
+        /// <param name="requireAdmin">A value indicating whether administrator role is required.</param>
+        public FirstTimeSetupRequirement(bool validateParentalSchedule = false, bool requireAdmin = true)
+        {
+            ValidateParentalSchedule = validateParentalSchedule;
+            RequireAdmin = requireAdmin;
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether administrator role is required.
+        /// </summary>
+        public bool RequireAdmin { get; }
+    }
+}

+ 0 - 44
Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs

@@ -1,44 +0,0 @@
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Library;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-
-namespace Jellyfin.Api.Auth.IgnoreParentalControlPolicy
-{
-    /// <summary>
-    /// Escape schedule controls handler.
-    /// </summary>
-    public class IgnoreParentalControlHandler : BaseAuthorizationHandler<IgnoreParentalControlRequirement>
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="IgnoreParentalControlHandler"/> class.
-        /// </summary>
-        /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
-        /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
-        /// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
-        public IgnoreParentalControlHandler(
-            IUserManager userManager,
-            INetworkManager networkManager,
-            IHttpContextAccessor httpContextAccessor)
-            : base(userManager, networkManager, httpContextAccessor)
-        {
-        }
-
-        /// <inheritdoc />
-        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IgnoreParentalControlRequirement requirement)
-        {
-            var validated = ValidateClaims(context.User, ignoreSchedule: true);
-            if (validated)
-            {
-                context.Succeed(requirement);
-            }
-            else
-            {
-                context.Fail();
-            }
-
-            return Task.CompletedTask;
-        }
-    }
-}

+ 0 - 11
Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlRequirement.cs

@@ -1,11 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-
-namespace Jellyfin.Api.Auth.IgnoreParentalControlPolicy
-{
-    /// <summary>
-    /// Escape schedule controls requirement.
-    /// </summary>
-    public class IgnoreParentalControlRequirement : IAuthorizationRequirement
-    {
-    }
-}

+ 0 - 45
Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs

@@ -1,45 +0,0 @@
-using System.Threading.Tasks;
-using Jellyfin.Api.Constants;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Library;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-
-namespace Jellyfin.Api.Auth.LocalAccessOrRequiresElevationPolicy
-{
-    /// <summary>
-    /// Local access or require elevated privileges handler.
-    /// </summary>
-    public class LocalAccessOrRequiresElevationHandler : BaseAuthorizationHandler<LocalAccessOrRequiresElevationRequirement>
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="LocalAccessOrRequiresElevationHandler"/> class.
-        /// </summary>
-        /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
-        /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
-        /// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
-        public LocalAccessOrRequiresElevationHandler(
-            IUserManager userManager,
-            INetworkManager networkManager,
-            IHttpContextAccessor httpContextAccessor)
-            : base(userManager, networkManager, httpContextAccessor)
-        {
-        }
-
-        /// <inheritdoc />
-        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, LocalAccessOrRequiresElevationRequirement requirement)
-        {
-            var validated = ValidateClaims(context.User, localAccessOnly: true);
-            if (validated || context.User.IsInRole(UserRoles.Administrator))
-            {
-                context.Succeed(requirement);
-            }
-            else
-            {
-                context.Fail();
-            }
-
-            return Task.CompletedTask;
-        }
-    }
-}

+ 0 - 11
Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationRequirement.cs

@@ -1,11 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-
-namespace Jellyfin.Api.Auth.LocalAccessOrRequiresElevationPolicy
-{
-    /// <summary>
-    /// The local access or elevated privileges authorization requirement.
-    /// </summary>
-    public class LocalAccessOrRequiresElevationRequirement : IAuthorizationRequirement
-    {
-    }
-}

+ 0 - 44
Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs

@@ -1,44 +0,0 @@
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Library;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-
-namespace Jellyfin.Api.Auth.LocalAccessPolicy
-{
-    /// <summary>
-    /// Local access handler.
-    /// </summary>
-    public class LocalAccessHandler : BaseAuthorizationHandler<LocalAccessRequirement>
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="LocalAccessHandler"/> class.
-        /// </summary>
-        /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
-        /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
-        /// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
-        public LocalAccessHandler(
-            IUserManager userManager,
-            INetworkManager networkManager,
-            IHttpContextAccessor httpContextAccessor)
-            : base(userManager, networkManager, httpContextAccessor)
-        {
-        }
-
-        /// <inheritdoc />
-        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, LocalAccessRequirement requirement)
-        {
-            var validated = ValidateClaims(context.User, localAccessOnly: true);
-            if (validated)
-            {
-                context.Succeed(requirement);
-            }
-            else
-            {
-                context.Fail();
-            }
-
-            return Task.CompletedTask;
-        }
-    }
-}

+ 0 - 11
Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessRequirement.cs

@@ -1,11 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-
-namespace Jellyfin.Api.Auth.LocalAccessPolicy
-{
-    /// <summary>
-    /// The local access authorization requirement.
-    /// </summary>
-    public class LocalAccessRequirement : IAuthorizationRequirement
-    {
-    }
-}

+ 0 - 45
Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs

@@ -1,45 +0,0 @@
-using System.Threading.Tasks;
-using Jellyfin.Api.Constants;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Library;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-
-namespace Jellyfin.Api.Auth.RequiresElevationPolicy
-{
-    /// <summary>
-    /// Authorization handler for requiring elevated privileges.
-    /// </summary>
-    public class RequiresElevationHandler : BaseAuthorizationHandler<RequiresElevationRequirement>
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="RequiresElevationHandler"/> class.
-        /// </summary>
-        /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
-        /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
-        /// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
-        public RequiresElevationHandler(
-            IUserManager userManager,
-            INetworkManager networkManager,
-            IHttpContextAccessor httpContextAccessor)
-            : base(userManager, networkManager, httpContextAccessor)
-        {
-        }
-
-        /// <inheritdoc />
-        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RequiresElevationRequirement requirement)
-        {
-            var validated = ValidateClaims(context.User);
-            if (validated && context.User.IsInRole(UserRoles.Administrator))
-            {
-                context.Succeed(requirement);
-            }
-            else
-            {
-                context.Fail();
-            }
-
-            return Task.CompletedTask;
-        }
-    }
-}

+ 0 - 11
Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationRequirement.cs

@@ -1,11 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-
-namespace Jellyfin.Api.Auth.RequiresElevationPolicy
-{
-    /// <summary>
-    /// The authorization requirement for requiring elevated privileges in the authorization handler.
-    /// </summary>
-    public class RequiresElevationRequirement : IAuthorizationRequirement
-    {
-    }
-}

+ 3 - 38
Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs

@@ -1,19 +1,16 @@
 using System.Threading.Tasks;
 using Jellyfin.Api.Extensions;
-using Jellyfin.Api.Helpers;
 using Jellyfin.Data.Enums;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.SyncPlay;
 using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
 
 namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
 {
     /// <summary>
     /// Default authorization handler.
     /// </summary>
-    public class SyncPlayAccessHandler : BaseAuthorizationHandler<SyncPlayAccessRequirement>
+    public class SyncPlayAccessHandler : AuthorizationHandler<SyncPlayAccessRequirement>
     {
         private readonly ISyncPlayManager _syncPlayManager;
         private readonly IUserManager _userManager;
@@ -23,14 +20,9 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
         /// </summary>
         /// <param name="syncPlayManager">Instance of the <see cref="ISyncPlayManager"/> interface.</param>
         /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
-        /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
-        /// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
         public SyncPlayAccessHandler(
             ISyncPlayManager syncPlayManager,
-            IUserManager userManager,
-            INetworkManager networkManager,
-            IHttpContextAccessor httpContextAccessor)
-            : base(userManager, networkManager, httpContextAccessor)
+            IUserManager userManager)
         {
             _syncPlayManager = syncPlayManager;
             _userManager = userManager;
@@ -39,27 +31,16 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
         /// <inheritdoc />
         protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SyncPlayAccessRequirement requirement)
         {
-            if (!ValidateClaims(context.User))
-            {
-                context.Fail();
-                return Task.CompletedTask;
-            }
-
             var userId = context.User.GetUserId();
             var user = _userManager.GetUserById(userId);
 
             if (requirement.RequiredAccess == SyncPlayAccessRequirementType.HasAccess)
             {
-                if (user.SyncPlayAccess == SyncPlayUserAccessType.CreateAndJoinGroups
-                    || user.SyncPlayAccess == SyncPlayUserAccessType.JoinGroups
+                if (user.SyncPlayAccess is SyncPlayUserAccessType.CreateAndJoinGroups or SyncPlayUserAccessType.JoinGroups
                     || _syncPlayManager.IsUserActive(userId))
                 {
                     context.Succeed(requirement);
                 }
-                else
-                {
-                    context.Fail();
-                }
             }
             else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.CreateGroup)
             {
@@ -67,10 +48,6 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
                 {
                     context.Succeed(requirement);
                 }
-                else
-                {
-                    context.Fail();
-                }
             }
             else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.JoinGroup)
             {
@@ -79,10 +56,6 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
                 {
                     context.Succeed(requirement);
                 }
-                else
-                {
-                    context.Fail();
-                }
             }
             else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.IsInGroup)
             {
@@ -90,14 +63,6 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
                 {
                     context.Succeed(requirement);
                 }
-                else
-                {
-                    context.Fail();
-                }
-            }
-            else
-            {
-                context.Fail();
             }
 
             return Task.CompletedTask;

+ 3 - 3
Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs

@@ -1,12 +1,12 @@
-using Jellyfin.Data.Enums;
-using Microsoft.AspNetCore.Authorization;
+using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
+using Jellyfin.Data.Enums;
 
 namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
 {
     /// <summary>
     /// The default authorization requirement.
     /// </summary>
-    public class SyncPlayAccessRequirement : IAuthorizationRequirement
+    public class SyncPlayAccessRequirement : DefaultAuthorizationRequirement
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="SyncPlayAccessRequirement"/> class.

+ 37 - 0
Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs

@@ -0,0 +1,37 @@
+using System.Threading.Tasks;
+using Jellyfin.Api.Auth.DownloadPolicy;
+using Jellyfin.Api.Extensions;
+using MediaBrowser.Controller.Library;
+using Microsoft.AspNetCore.Authorization;
+
+namespace Jellyfin.Api.Auth.UserPermissionPolicy
+{
+    /// <summary>
+    /// Download authorization handler.
+    /// </summary>
+    public class UserPermissionHandler : AuthorizationHandler<UserPermissionRequirement>
+    {
+        private readonly IUserManager _userManager;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="UserPermissionHandler"/> class.
+        /// </summary>
+        /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+        public UserPermissionHandler(IUserManager userManager)
+        {
+            _userManager = userManager;
+        }
+
+        /// <inheritdoc />
+        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserPermissionRequirement requirement)
+        {
+            var user = _userManager.GetUserById(context.User.GetUserId());
+            if (user.HasPermission(requirement.RequiredPermission))
+            {
+                context.Succeed(requirement);
+            }
+
+            return Task.CompletedTask;
+        }
+    }
+}

+ 26 - 0
Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs

@@ -0,0 +1,26 @@
+using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
+using Jellyfin.Data.Enums;
+
+namespace Jellyfin.Api.Auth.DownloadPolicy
+{
+    /// <summary>
+    /// The user permission requirement.
+    /// </summary>
+    public class UserPermissionRequirement : DefaultAuthorizationRequirement
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="UserPermissionRequirement"/> class.
+        /// </summary>
+        /// <param name="requiredPermission">The required <see cref="PermissionKind"/>.</param>
+        /// <param name="validateParentalSchedule">Whether to validate the user's parental schedule.</param>
+        public UserPermissionRequirement(PermissionKind requiredPermission, bool validateParentalSchedule = true) : base(validateParentalSchedule)
+        {
+            RequiredPermission = requiredPermission;
+        }
+
+        /// <summary>
+        /// Gets the required user permission.
+        /// </summary>
+        public PermissionKind RequiredPermission { get; }
+    }
+}

+ 0 - 5
Jellyfin.Api/Constants/Policies.cs

@@ -5,11 +5,6 @@ namespace Jellyfin.Api.Constants;
 /// </summary>
 public static class Policies
 {
-    /// <summary>
-    /// Policy name for default authorization.
-    /// </summary>
-    public const string DefaultAuthorization = "DefaultAuthorization";
-
     /// <summary>
     /// Policy name for requiring first time setup or elevated privileges.
     /// </summary>

+ 1 - 2
Jellyfin.Api/Controllers/ArtistsController.cs

@@ -1,7 +1,6 @@
 using System;
 using System.ComponentModel.DataAnnotations;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
@@ -23,7 +22,7 @@ namespace Jellyfin.Api.Controllers;
 /// The artists controller.
 /// </summary>
 [Route("Artists")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class ArtistsController : BaseJellyfinApiController
 {
     private readonly ILibraryManager _libraryManager;

+ 1 - 2
Jellyfin.Api/Controllers/ChannelsController.cs

@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 using System.Threading;
 using System.Threading.Tasks;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Enums;
@@ -23,7 +22,7 @@ namespace Jellyfin.Api.Controllers;
 /// <summary>
 /// Channels Controller.
 /// </summary>
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class ChannelsController : BaseJellyfinApiController
 {
     private readonly IChannelManager _channelManager;

+ 1 - 2
Jellyfin.Api/Controllers/ClientLogController.cs

@@ -1,7 +1,6 @@
 using System.Net.Mime;
 using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Models.ClientLogDtos;
 using MediaBrowser.Controller.ClientEvent;
@@ -15,7 +14,7 @@ namespace Jellyfin.Api.Controllers;
 /// <summary>
 /// Client log controller.
 /// </summary>
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class ClientLogController : BaseJellyfinApiController
 {
     private const int MaxDocumentSize = 1_000_000;

+ 1 - 2
Jellyfin.Api/Controllers/CollectionController.cs

@@ -1,7 +1,6 @@
 using System;
 using System.ComponentModel.DataAnnotations;
 using System.Threading.Tasks;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.ModelBinders;
 using MediaBrowser.Controller.Collections;
@@ -17,7 +16,7 @@ namespace Jellyfin.Api.Controllers;
 /// The collection controller.
 /// </summary>
 [Route("Collections")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class CollectionController : BaseJellyfinApiController
 {
     private readonly ICollectionManager _collectionManager;

+ 1 - 1
Jellyfin.Api/Controllers/ConfigurationController.cs

@@ -19,7 +19,7 @@ namespace Jellyfin.Api.Controllers;
 /// Configuration Controller.
 /// </summary>
 [Route("System")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class ConfigurationController : BaseJellyfinApiController
 {
     private readonly IServerConfigurationManager _configurationManager;

+ 1 - 2
Jellyfin.Api/Controllers/DashboardController.cs

@@ -4,7 +4,6 @@ using System.IO;
 using System.Linq;
 using System.Net.Mime;
 using Jellyfin.Api.Attributes;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Models;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Model.Net;
@@ -48,7 +47,7 @@ public class DashboardController : BaseJellyfinApiController
     [HttpGet("web/ConfigurationPages")]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     public ActionResult<IEnumerable<ConfigurationPageInfo>> GetConfigurationPages(
         [FromQuery] bool? enableInMainMenu)
     {

+ 1 - 2
Jellyfin.Api/Controllers/DisplayPreferencesController.cs

@@ -3,7 +3,6 @@ using System.ComponentModel.DataAnnotations;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
 using MediaBrowser.Common.Extensions;
@@ -19,7 +18,7 @@ namespace Jellyfin.Api.Controllers;
 /// <summary>
 /// Display Preferences Controller.
 /// </summary>
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class DisplayPreferencesController : BaseJellyfinApiController
 {
     private readonly IDisplayPreferencesManager _displayPreferencesManager;

+ 1 - 2
Jellyfin.Api/Controllers/DynamicHlsController.cs

@@ -9,7 +9,6 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.Models.PlaybackDtos;
 using Jellyfin.Api.Models.StreamingDtos;
@@ -36,7 +35,7 @@ namespace Jellyfin.Api.Controllers;
 /// Dynamic hls controller.
 /// </summary>
 [Route("")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class DynamicHlsController : BaseJellyfinApiController
 {
     private const string DefaultVodEncoderPreset = "veryfast";

+ 1 - 2
Jellyfin.Api/Controllers/FilterController.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Dto;
@@ -18,7 +17,7 @@ namespace Jellyfin.Api.Controllers;
 /// Filters controller.
 /// </summary>
 [Route("")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class FilterController : BaseJellyfinApiController
 {
     private readonly ILibraryManager _libraryManager;

+ 2 - 3
Jellyfin.Api/Controllers/GenresController.cs

@@ -1,7 +1,6 @@
 using System;
 using System.ComponentModel.DataAnnotations;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
@@ -23,7 +22,7 @@ namespace Jellyfin.Api.Controllers;
 /// <summary>
 /// The genres controller.
 /// </summary>
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class GenresController : BaseJellyfinApiController
 {
     private readonly IUserManager _userManager;
@@ -132,7 +131,7 @@ public class GenresController : BaseJellyfinApiController
         QueryResult<(BaseItem, ItemCounts)> result;
         if (parentItem is ICollectionFolder parentCollectionFolder
             && (string.Equals(parentCollectionFolder.CollectionType, CollectionType.Music, StringComparison.Ordinal)
-            || string.Equals(parentCollectionFolder.CollectionType, CollectionType.MusicVideos, StringComparison.Ordinal)))
+                || string.Equals(parentCollectionFolder.CollectionType, CollectionType.MusicVideos, StringComparison.Ordinal)))
         {
             result = _libraryManager.GetMusicGenres(query);
         }

+ 2 - 3
Jellyfin.Api/Controllers/HlsSegmentController.cs

@@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis;
 using System.IO;
 using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Helpers;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Configuration;
@@ -80,7 +79,7 @@ public class HlsSegmentController : BaseJellyfinApiController
     /// <response code="200">Hls video playlist returned.</response>
     /// <returns>A <see cref="FileStreamResult"/> containing the playlist.</returns>
     [HttpGet("Videos/{itemId}/hls/{playlistId}/stream.m3u8")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesPlaylistFile]
     [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")]
@@ -106,7 +105,7 @@ public class HlsSegmentController : BaseJellyfinApiController
     /// <response code="204">Encoding stopped successfully.</response>
     /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
     [HttpDelete("Videos/ActiveEncodings")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public ActionResult StopEncodingProcess(
         [FromQuery, Required] string deviceId,

+ 9 - 9
Jellyfin.Api/Controllers/ImageController.cs

@@ -88,7 +88,7 @@ public class ImageController : BaseJellyfinApiController
     /// <response code="403">User does not have permission to delete the image.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Users/{userId}/Images/{imageType}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [AcceptsImageFile]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
@@ -137,7 +137,7 @@ public class ImageController : BaseJellyfinApiController
     /// <response code="403">User does not have permission to delete the image.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Users/{userId}/Images/{imageType}/{index}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [AcceptsImageFile]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
@@ -186,7 +186,7 @@ public class ImageController : BaseJellyfinApiController
     /// <response code="403">User does not have permission to delete the image.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpDelete("Users/{userId}/Images/{imageType}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
     [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
@@ -230,7 +230,7 @@ public class ImageController : BaseJellyfinApiController
     /// <response code="403">User does not have permission to delete the image.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpDelete("Users/{userId}/Images/{imageType}/{index}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
     [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
@@ -432,7 +432,7 @@ public class ImageController : BaseJellyfinApiController
     /// <response code="404">Item not found.</response>
     /// <returns>The list of image infos on success, or <see cref="NotFoundResult"/> if item not found.</returns>
     [HttpGet("Items/{itemId}/Images")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     public async Task<ActionResult<IEnumerable<ImageInfo>>> GetItemImageInfos([FromRoute, Required] Guid itemId)
@@ -1930,10 +1930,10 @@ public class ImageController : BaseJellyfinApiController
         }
 
         var responseHeaders = new Dictionary<string, string>
-            {
-                { "transferMode.dlna.org", "Interactive" },
-                { "realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*" }
-            };
+        {
+            { "transferMode.dlna.org", "Interactive" },
+            { "realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*" }
+        };
 
         if (!imageInfo.IsLocalFile && item is not null)
         {

+ 1 - 2
Jellyfin.Api/Controllers/InstantMixController.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Entities;
@@ -22,7 +21,7 @@ namespace Jellyfin.Api.Controllers;
 /// The instant mix controller.
 /// </summary>
 [Route("")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class InstantMixController : BaseJellyfinApiController
 {
     private readonly IUserManager _userManager;

+ 1 - 1
Jellyfin.Api/Controllers/ItemLookupController.cs

@@ -23,7 +23,7 @@ namespace Jellyfin.Api.Controllers;
 /// Item lookup controller.
 /// </summary>
 [Route("")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class ItemLookupController : BaseJellyfinApiController
 {
     private readonly IProviderManager _providerManager;

+ 1 - 2
Jellyfin.Api/Controllers/ItemsController.cs

@@ -1,7 +1,6 @@
 using System;
 using System.ComponentModel.DataAnnotations;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
@@ -25,7 +24,7 @@ namespace Jellyfin.Api.Controllers;
 /// The items controller.
 /// </summary>
 [Route("")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class ItemsController : BaseJellyfinApiController
 {
     private readonly IUserManager _userManager;

+ 40 - 40
Jellyfin.Api/Controllers/LibraryController.cs

@@ -95,7 +95,7 @@ public class LibraryController : BaseJellyfinApiController
     /// <response code="404">Item not found.</response>
     /// <returns>A <see cref="FileStreamResult"/> with the original file.</returns>
     [HttpGet("Items/{itemId}/File")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     [ProducesFile("video/*", "audio/*")]
@@ -116,7 +116,7 @@ public class LibraryController : BaseJellyfinApiController
     /// <response code="200">Critic reviews returned.</response>
     /// <returns>The list of critic reviews.</returns>
     [HttpGet("Items/{itemId}/CriticReviews")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [Obsolete("This endpoint is obsolete.")]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public ActionResult<QueryResult<BaseItemDto>> GetCriticReviews()
@@ -134,7 +134,7 @@ public class LibraryController : BaseJellyfinApiController
     /// <response code="404">Item not found.</response>
     /// <returns>The item theme songs.</returns>
     [HttpGet("Items/{itemId}/ThemeSongs")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     public ActionResult<ThemeMediaResult> GetThemeSongs(
@@ -200,7 +200,7 @@ public class LibraryController : BaseJellyfinApiController
     /// <response code="404">Item not found.</response>
     /// <returns>The item theme videos.</returns>
     [HttpGet("Items/{itemId}/ThemeVideos")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     public ActionResult<ThemeMediaResult> GetThemeVideos(
@@ -266,7 +266,7 @@ public class LibraryController : BaseJellyfinApiController
     /// <response code="404">Item not found.</response>
     /// <returns>The item theme videos.</returns>
     [HttpGet("Items/{itemId}/ThemeMedia")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public ActionResult<AllThemeMediaResult> GetThemeMedia(
         [FromRoute, Required] Guid itemId,
@@ -321,7 +321,7 @@ public class LibraryController : BaseJellyfinApiController
     /// <response code="401">Unauthorized access.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpDelete("Items/{itemId}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     public ActionResult DeleteItem(Guid itemId)
@@ -350,7 +350,7 @@ public class LibraryController : BaseJellyfinApiController
     /// <response code="401">Unauthorized access.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpDelete("Items")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     public ActionResult DeleteItems([FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] ids)
@@ -392,7 +392,7 @@ public class LibraryController : BaseJellyfinApiController
     /// <response code="200">Item counts returned.</response>
     /// <returns>Item counts.</returns>
     [HttpGet("Items/Counts")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public ActionResult<ItemCounts> GetItemCounts(
         [FromQuery] Guid? userId,
@@ -426,7 +426,7 @@ public class LibraryController : BaseJellyfinApiController
     /// <response code="404">Item not found.</response>
     /// <returns>Item parents.</returns>
     [HttpGet("Items/{itemId}/Ancestors")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     public ActionResult<IEnumerable<BaseItemDto>> GetAncestors([FromRoute, Required] Guid itemId, [FromQuery] Guid? userId)
@@ -509,7 +509,7 @@ public class LibraryController : BaseJellyfinApiController
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Library/Series/Added", Name = "PostAddedSeries")]
     [HttpPost("Library/Series/Updated")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public ActionResult PostUpdatedSeries([FromQuery] string? tvdbId)
     {
@@ -539,7 +539,7 @@ public class LibraryController : BaseJellyfinApiController
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Library/Movies/Added", Name = "PostAddedMovies")]
     [HttpPost("Library/Movies/Updated")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public ActionResult PostUpdatedMovies([FromQuery] string? tmdbId, [FromQuery] string? imdbId)
     {
@@ -580,7 +580,7 @@ public class LibraryController : BaseJellyfinApiController
     /// <response code="204">Report success.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Library/Media/Updated")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public ActionResult PostUpdatedMedia([FromBody, Required] MediaUpdateInfoDto dto)
     {
@@ -657,7 +657,7 @@ public class LibraryController : BaseJellyfinApiController
     [HttpGet("Shows/{itemId}/Similar", Name = "GetSimilarShows")]
     [HttpGet("Movies/{itemId}/Similar", Name = "GetSimilarMovies")]
     [HttpGet("Trailers/{itemId}/Similar", Name = "GetSimilarTrailers")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public ActionResult<QueryResult<BaseItemDto>> GetSimilarItems(
         [FromRoute, Required] Guid itemId,
@@ -802,32 +802,32 @@ public class LibraryController : BaseJellyfinApiController
                 Type = type,
 
                 MetadataFetchers = plugins
-                .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
-                .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataFetcher))
-                .Select(i => new LibraryOptionInfoDto
-                {
-                    Name = i.Name,
-                    DefaultEnabled = IsMetadataFetcherEnabledByDefault(i.Name, type, isNewLibrary)
-                })
-                .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
-                .ToArray(),
+                    .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
+                    .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataFetcher))
+                    .Select(i => new LibraryOptionInfoDto
+                    {
+                        Name = i.Name,
+                        DefaultEnabled = IsMetadataFetcherEnabledByDefault(i.Name, type, isNewLibrary)
+                    })
+                    .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+                    .ToArray(),
 
                 ImageFetchers = plugins
-                .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
-                .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.ImageFetcher))
-                .Select(i => new LibraryOptionInfoDto
-                {
-                    Name = i.Name,
-                    DefaultEnabled = IsImageFetcherEnabledByDefault(i.Name, type, isNewLibrary)
-                })
-                .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
-                .ToArray(),
+                    .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
+                    .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.ImageFetcher))
+                    .Select(i => new LibraryOptionInfoDto
+                    {
+                        Name = i.Name,
+                        DefaultEnabled = IsImageFetcherEnabledByDefault(i.Name, type, isNewLibrary)
+                    })
+                    .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+                    .ToArray(),
 
                 SupportedImageTypes = plugins
-                .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
-                .SelectMany(i => i.SupportedImageTypes ?? Array.Empty<ImageType>())
-                .Distinct()
-                .ToArray(),
+                    .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
+                    .SelectMany(i => i.SupportedImageTypes ?? Array.Empty<ImageType>())
+                    .Distinct()
+                    .ToArray(),
 
                 DefaultImageOptions = defaultImageOptions ?? Array.Empty<ImageOption>()
             });
@@ -920,13 +920,13 @@ public class LibraryController : BaseJellyfinApiController
             if (string.Equals(name, "TheMovieDb", StringComparison.OrdinalIgnoreCase))
             {
                 return !(string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase)
-                     || string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase)
-                     || string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase));
+                         || string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase)
+                         || string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase));
             }
 
             return string.Equals(name, "TheTVDB", StringComparison.OrdinalIgnoreCase)
-               || string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase)
-               || string.Equals(name, "MusicBrainz", StringComparison.OrdinalIgnoreCase);
+                   || string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase)
+                   || string.Equals(name, "MusicBrainz", StringComparison.OrdinalIgnoreCase);
         }
 
         var metadataOptions = _serverConfigurationManager.Configuration.MetadataOptions
@@ -934,7 +934,7 @@ public class LibraryController : BaseJellyfinApiController
             .ToArray();
 
         return metadataOptions.Length == 0
-           || metadataOptions.Any(i => !i.DisabledMetadataFetchers.Contains(name, StringComparison.OrdinalIgnoreCase));
+               || metadataOptions.Any(i => !i.DisabledMetadataFetchers.Contains(name, StringComparison.OrdinalIgnoreCase));
     }
 
     private bool IsImageFetcherEnabledByDefault(string name, string type, bool isNewLibrary)

+ 38 - 39
Jellyfin.Api/Controllers/LiveTvController.cs

@@ -10,7 +10,6 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
@@ -95,7 +94,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// </returns>
     [HttpGet("Info")]
     [ProducesResponseType(StatusCodes.Status200OK)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     public ActionResult<LiveTvInfo> GetLiveTvInfo()
     {
         return _liveTvManager.GetLiveTvInfo(CancellationToken.None);
@@ -131,7 +130,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// </returns>
     [HttpGet("Channels")]
     [ProducesResponseType(StatusCodes.Status200OK)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     public ActionResult<QueryResult<BaseItemDto>> GetLiveTvChannels(
         [FromQuery] ChannelType? type,
         [FromQuery] Guid? userId,
@@ -210,7 +209,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <returns>An <see cref="OkResult"/> containing the live tv channel.</returns>
     [HttpGet("Channels/{channelId}")]
     [ProducesResponseType(StatusCodes.Status200OK)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     public ActionResult<BaseItemDto> GetChannel([FromRoute, Required] Guid channelId, [FromQuery] Guid? userId)
     {
         var user = userId is null || userId.Value.Equals(default)
@@ -251,7 +250,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <returns>An <see cref="OkResult"/> containing the live tv recordings.</returns>
     [HttpGet("Recordings")]
     [ProducesResponseType(StatusCodes.Status200OK)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     public ActionResult<QueryResult<BaseItemDto>> GetRecordings(
         [FromQuery] string? channelId,
         [FromQuery] Guid? userId,
@@ -322,7 +321,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <returns>An <see cref="OkResult"/> containing the live tv recordings.</returns>
     [HttpGet("Recordings/Series")]
     [ProducesResponseType(StatusCodes.Status200OK)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [Obsolete("This endpoint is obsolete.")]
     [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "channelId", Justification = "Imported from ServiceStack")]
     [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Imported from ServiceStack")]
@@ -365,7 +364,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <returns>An <see cref="OkResult"/> containing the recording groups.</returns>
     [HttpGet("Recordings/Groups")]
     [ProducesResponseType(StatusCodes.Status200OK)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [Obsolete("This endpoint is obsolete.")]
     [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Imported from ServiceStack")]
     public ActionResult<QueryResult<BaseItemDto>> GetRecordingGroups([FromQuery] Guid? userId)
@@ -381,7 +380,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <returns>An <see cref="OkResult"/> containing the recording folders.</returns>
     [HttpGet("Recordings/Folders")]
     [ProducesResponseType(StatusCodes.Status200OK)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     public ActionResult<QueryResult<BaseItemDto>> GetRecordingFolders([FromQuery] Guid? userId)
     {
         var user = userId is null || userId.Value.Equals(default)
@@ -403,7 +402,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <returns>An <see cref="OkResult"/> containing the live tv recording.</returns>
     [HttpGet("Recordings/{recordingId}")]
     [ProducesResponseType(StatusCodes.Status200OK)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     public ActionResult<BaseItemDto> GetRecording([FromRoute, Required] Guid recordingId, [FromQuery] Guid? userId)
     {
         var user = userId is null || userId.Value.Equals(default)
@@ -425,7 +424,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Tuners/{tunerId}/Reset")]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     public async Task<ActionResult> ResetTuner([FromRoute, Required] string tunerId)
     {
         await AssertUserCanManageLiveTv().ConfigureAwait(false);
@@ -443,7 +442,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// </returns>
     [HttpGet("Timers/{timerId}")]
     [ProducesResponseType(StatusCodes.Status200OK)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     public async Task<ActionResult<TimerInfoDto>> GetTimer([FromRoute, Required] string timerId)
     {
         return await _liveTvManager.GetTimer(timerId, CancellationToken.None).ConfigureAwait(false);
@@ -459,7 +458,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// </returns>
     [HttpGet("Timers/Defaults")]
     [ProducesResponseType(StatusCodes.Status200OK)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     public async Task<ActionResult<SeriesTimerInfoDto>> GetDefaultTimer([FromQuery] string? programId)
     {
         return string.IsNullOrEmpty(programId)
@@ -479,7 +478,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// </returns>
     [HttpGet("Timers")]
     [ProducesResponseType(StatusCodes.Status200OK)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     public async Task<ActionResult<QueryResult<TimerInfoDto>>> GetTimers(
         [FromQuery] string? channelId,
         [FromQuery] string? seriesTimerId,
@@ -533,7 +532,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// </returns>
     [HttpGet("Programs")]
     [ProducesResponseType(StatusCodes.Status200OK)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     public async Task<ActionResult<QueryResult<BaseItemDto>>> GetLiveTvPrograms(
         [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] channelIds,
         [FromQuery] Guid? userId,
@@ -616,7 +615,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// </returns>
     [HttpPost("Programs")]
     [ProducesResponseType(StatusCodes.Status200OK)]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     public async Task<ActionResult<QueryResult<BaseItemDto>>> GetPrograms([FromBody] GetProgramsDto body)
     {
         var user = body.UserId.Equals(default) ? null : _userManager.GetUserById(body.UserId);
@@ -682,7 +681,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="200">Recommended epgs returned.</response>
     /// <returns>A <see cref="OkResult"/> containing the queryresult of recommended epgs.</returns>
     [HttpGet("Programs/Recommended")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public async Task<ActionResult<QueryResult<BaseItemDto>>> GetRecommendedPrograms(
         [FromQuery] Guid? userId,
@@ -734,7 +733,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="200">Program returned.</response>
     /// <returns>An <see cref="OkResult"/> containing the livetv program.</returns>
     [HttpGet("Programs/{programId}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public async Task<ActionResult<BaseItemDto>> GetProgram(
         [FromRoute, Required] string programId,
@@ -755,7 +754,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="404">Item not found.</response>
     /// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
     [HttpDelete("Recordings/{recordingId}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     public async Task<ActionResult> DeleteRecording([FromRoute, Required] Guid recordingId)
@@ -783,7 +782,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="204">Timer deleted.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpDelete("Timers/{timerId}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> CancelTimer([FromRoute, Required] string timerId)
     {
@@ -800,7 +799,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="204">Timer updated.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Timers/{timerId}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "timerId", Justification = "Imported from ServiceStack")]
     public async Task<ActionResult> UpdateTimer([FromRoute, Required] string timerId, [FromBody] TimerInfoDto timerInfo)
@@ -817,7 +816,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="204">Timer created.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Timers")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> CreateTimer([FromBody] TimerInfoDto timerInfo)
     {
@@ -834,7 +833,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="404">Series timer not found.</response>
     /// <returns>A <see cref="OkResult"/> on success, or a <see cref="NotFoundResult"/> if timer not found.</returns>
     [HttpGet("SeriesTimers/{timerId}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     public async Task<ActionResult<SeriesTimerInfoDto>> GetSeriesTimer([FromRoute, Required] string timerId)
@@ -856,7 +855,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="200">Timers returned.</response>
     /// <returns>An <see cref="OkResult"/> of live tv series timers.</returns>
     [HttpGet("SeriesTimers")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public async Task<ActionResult<QueryResult<SeriesTimerInfoDto>>> GetSeriesTimers([FromQuery] string? sortBy, [FromQuery] SortOrder? sortOrder)
     {
@@ -876,7 +875,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="204">Timer cancelled.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpDelete("SeriesTimers/{timerId}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> CancelSeriesTimer([FromRoute, Required] string timerId)
     {
@@ -893,7 +892,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="204">Series timer updated.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("SeriesTimers/{timerId}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "timerId", Justification = "Imported from ServiceStack")]
     public async Task<ActionResult> UpdateSeriesTimer([FromRoute, Required] string timerId, [FromBody] SeriesTimerInfoDto seriesTimerInfo)
@@ -910,7 +909,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="204">Series timer info created.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("SeriesTimers")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> CreateSeriesTimer([FromBody] SeriesTimerInfoDto seriesTimerInfo)
     {
@@ -925,7 +924,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <param name="groupId">Group id.</param>
     /// <returns>A <see cref="NotFoundResult"/>.</returns>
     [HttpGet("Recordings/Groups/{groupId}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     [Obsolete("This endpoint is obsolete.")]
     public ActionResult<BaseItemDto> GetRecordingGroup([FromRoute, Required] Guid groupId)
@@ -939,7 +938,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="200">Guid info returned.</response>
     /// <returns>An <see cref="OkResult"/> containing the guide info.</returns>
     [HttpGet("GuideInfo")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public ActionResult<GuideInfo> GetGuideInfo()
     {
@@ -953,7 +952,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="200">Created tuner host returned.</response>
     /// <returns>A <see cref="OkResult"/> containing the created tuner host.</returns>
     [HttpPost("TunerHosts")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public async Task<ActionResult<TunerHostInfo>> AddTunerHost([FromBody] TunerHostInfo tunerHostInfo)
     {
@@ -967,7 +966,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="204">Tuner host deleted.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpDelete("TunerHosts")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public ActionResult DeleteTunerHost([FromQuery] string? id)
     {
@@ -983,7 +982,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="200">Default listings provider info returned.</response>
     /// <returns>An <see cref="OkResult"/> containing the default listings provider info.</returns>
     [HttpGet("ListingProviders/Default")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public ActionResult<ListingsProviderInfo> GetDefaultListingProvider()
     {
@@ -1000,7 +999,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="200">Created listings provider returned.</response>
     /// <returns>A <see cref="OkResult"/> containing the created listings provider.</returns>
     [HttpPost("ListingProviders")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [SuppressMessage("Microsoft.Performance", "CA5350:RemoveSha1", MessageId = "AddListingProvider", Justification = "Imported from ServiceStack")]
     public async Task<ActionResult<ListingsProviderInfo>> AddListingProvider(
@@ -1026,7 +1025,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="204">Listing provider deleted.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpDelete("ListingProviders")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public ActionResult DeleteListingProvider([FromQuery] string? id)
     {
@@ -1044,7 +1043,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="200">Available lineups returned.</response>
     /// <returns>A <see cref="OkResult"/> containing the available lineups.</returns>
     [HttpGet("ListingProviders/Lineups")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public async Task<ActionResult<IEnumerable<NameIdPair>>> GetLineups(
         [FromQuery] string? id,
@@ -1061,7 +1060,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="200">Available countries returned.</response>
     /// <returns>A <see cref="FileResult"/> containing the available countries.</returns>
     [HttpGet("ListingProviders/SchedulesDirect/Countries")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesFile(MediaTypeNames.Application.Json)]
     public async Task<ActionResult> GetSchedulesDirectCountries()
@@ -1082,7 +1081,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="200">Channel mapping options returned.</response>
     /// <returns>An <see cref="OkResult"/> containing the channel mapping options.</returns>
     [HttpGet("ChannelMappingOptions")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public async Task<ActionResult<ChannelMappingOptionsDto>> GetChannelMappingOptions([FromQuery] string? providerId)
     {
@@ -1120,7 +1119,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="200">Created channel mapping returned.</response>
     /// <returns>An <see cref="OkResult"/> containing the created channel mapping.</returns>
     [HttpPost("ChannelMappings")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public async Task<ActionResult<TunerChannelMapping>> SetChannelMapping([FromBody, Required] SetChannelMappingDto setChannelMappingDto)
     {
@@ -1133,7 +1132,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <response code="200">Tuner host types returned.</response>
     /// <returns>An <see cref="OkResult"/> containing the tuner host types.</returns>
     [HttpGet("TunerHosts/Types")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public ActionResult<IEnumerable<NameIdPair>> GetTunerHostTypes()
     {
@@ -1148,7 +1147,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <returns>An <see cref="OkResult"/> containing the tuners.</returns>
     [HttpGet("Tuners/Discvover", Name = "DiscvoverTuners")]
     [HttpGet("Tuners/Discover")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public async Task<ActionResult<IEnumerable<TunerHostInfo>>> DiscoverTuners([FromQuery] bool newDevicesOnly = false)
     {

+ 1 - 2
Jellyfin.Api/Controllers/MediaInfoController.cs

@@ -5,7 +5,6 @@ using System.Linq;
 using System.Net.Mime;
 using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.Models.MediaInfoDtos;
@@ -25,7 +24,7 @@ namespace Jellyfin.Api.Controllers;
 /// The media info controller.
 /// </summary>
 [Route("")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class MediaInfoController : BaseJellyfinApiController
 {
     private readonly IMediaSourceManager _mediaSourceManager;

+ 1 - 2
Jellyfin.Api/Controllers/MoviesController.cs

@@ -2,7 +2,6 @@ using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Entities;
@@ -23,7 +22,7 @@ namespace Jellyfin.Api.Controllers;
 /// <summary>
 /// Movies controller.
 /// </summary>
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class MoviesController : BaseJellyfinApiController
 {
     private readonly IUserManager _userManager;

+ 1 - 2
Jellyfin.Api/Controllers/MusicGenresController.cs

@@ -1,7 +1,6 @@
 using System;
 using System.ComponentModel.DataAnnotations;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
@@ -23,7 +22,7 @@ namespace Jellyfin.Api.Controllers;
 /// <summary>
 /// The music genres controller.
 /// </summary>
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class MusicGenresController : BaseJellyfinApiController
 {
     private readonly ILibraryManager _libraryManager;

+ 1 - 1
Jellyfin.Api/Controllers/PackageController.cs

@@ -17,7 +17,7 @@ namespace Jellyfin.Api.Controllers;
 /// Package Controller.
 /// </summary>
 [Route("")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class PackageController : BaseJellyfinApiController
 {
     private readonly IInstallationManager _installationManager;

+ 1 - 2
Jellyfin.Api/Controllers/PersonsController.cs

@@ -1,7 +1,6 @@
 using System;
 using System.ComponentModel.DataAnnotations;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Entities;
@@ -20,7 +19,7 @@ namespace Jellyfin.Api.Controllers;
 /// <summary>
 /// Persons controller.
 /// </summary>
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class PersonsController : BaseJellyfinApiController
 {
     private readonly ILibraryManager _libraryManager;

+ 1 - 2
Jellyfin.Api/Controllers/PlaylistsController.cs

@@ -4,7 +4,6 @@ using System.ComponentModel.DataAnnotations;
 using System.Linq;
 using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Api.Models.PlaylistDtos;
@@ -25,7 +24,7 @@ namespace Jellyfin.Api.Controllers;
 /// <summary>
 /// Playlists controller.
 /// </summary>
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class PlaylistsController : BaseJellyfinApiController
 {
     private readonly IPlaylistManager _playlistManager;

+ 1 - 2
Jellyfin.Api/Controllers/PlaystateController.cs

@@ -2,7 +2,6 @@ using System;
 using System.ComponentModel.DataAnnotations;
 using System.Diagnostics.CodeAnalysis;
 using System.Threading.Tasks;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
@@ -23,7 +22,7 @@ namespace Jellyfin.Api.Controllers;
 /// Playstate controller.
 /// </summary>
 [Route("")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class PlaystateController : BaseJellyfinApiController
 {
     private readonly IUserManager _userManager;

+ 1 - 1
Jellyfin.Api/Controllers/PluginsController.cs

@@ -21,7 +21,7 @@ namespace Jellyfin.Api.Controllers;
 /// <summary>
 /// Plugins controller.
 /// </summary>
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class PluginsController : BaseJellyfinApiController
 {
     private readonly IInstallationManager _installationManager;

+ 1 - 1
Jellyfin.Api/Controllers/QuickConnectController.cs

@@ -111,7 +111,7 @@ public class QuickConnectController : BaseJellyfinApiController
     /// <response code="403">Unknown user id.</response>
     /// <returns>Boolean indicating if the authorization was successful.</returns>
     [HttpPost("Authorize")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     public async Task<ActionResult<bool>> AuthorizeQuickConnect([FromQuery, Required] string code, [FromQuery] Guid? userId = null)

+ 2 - 2
Jellyfin.Api/Controllers/RemoteImageController.cs

@@ -56,7 +56,7 @@ public class RemoteImageController : BaseJellyfinApiController
     /// <response code="404">Item not found.</response>
     /// <returns>Remote Image Result.</returns>
     [HttpGet("Items/{itemId}/RemoteImages")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     public async Task<ActionResult<RemoteImageResult>> GetRemoteImages(
@@ -121,7 +121,7 @@ public class RemoteImageController : BaseJellyfinApiController
     /// <response code="404">Item not found.</response>
     /// <returns>List of remote image providers.</returns>
     [HttpGet("Items/{itemId}/RemoteImages/Providers")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     public ActionResult<IEnumerable<ImageProviderInfo>> GetRemoteImageProviders([FromRoute, Required] Guid itemId)

+ 1 - 2
Jellyfin.Api/Controllers/SearchController.cs

@@ -3,7 +3,6 @@ using System.ComponentModel;
 using System.ComponentModel.DataAnnotations;
 using System.Globalization;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
@@ -26,7 +25,7 @@ namespace Jellyfin.Api.Controllers;
 /// Search controller.
 /// </summary>
 [Route("Search/Hints")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class SearchController : BaseJellyfinApiController
 {
     private readonly ISearchEngine _searchEngine;

+ 14 - 14
Jellyfin.Api/Controllers/SessionController.cs

@@ -56,7 +56,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="200">List of sessions returned.</response>
     /// <returns>An <see cref="IEnumerable{SessionInfo}"/> with the available sessions.</returns>
     [HttpGet("Sessions")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public ActionResult<IEnumerable<SessionInfo>> GetSessions(
         [FromQuery] Guid? controllableByUserId,
@@ -119,7 +119,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="204">Instruction sent to session.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Sessions/{sessionId}/Viewing")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> DisplayContent(
         [FromRoute, Required] string sessionId,
@@ -158,7 +158,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="204">Instruction sent to session.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Sessions/{sessionId}/Playing")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> Play(
         [FromRoute, Required] string sessionId,
@@ -201,7 +201,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="204">Playstate command sent to session.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Sessions/{sessionId}/Playing/{command}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> SendPlaystateCommand(
         [FromRoute, Required] string sessionId,
@@ -232,7 +232,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="204">System command sent to session.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Sessions/{sessionId}/System/{command}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> SendSystemCommand(
         [FromRoute, Required] string sessionId,
@@ -258,7 +258,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="204">General command sent to session.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Sessions/{sessionId}/Command/{command}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> SendGeneralCommand(
         [FromRoute, Required] string sessionId,
@@ -286,7 +286,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="204">Full general command sent to session.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Sessions/{sessionId}/Command")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> SendFullGeneralCommand(
         [FromRoute, Required] string sessionId,
@@ -316,7 +316,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="204">Message sent.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Sessions/{sessionId}/Message")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> SendMessageCommand(
         [FromRoute, Required] string sessionId,
@@ -345,7 +345,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="204">User added to session.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Sessions/{sessionId}/User/{userId}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public ActionResult AddUserToSession(
         [FromRoute, Required] string sessionId,
@@ -363,7 +363,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="204">User removed from session.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpDelete("Sessions/{sessionId}/User/{userId}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public ActionResult RemoveUserFromSession(
         [FromRoute, Required] string sessionId,
@@ -385,7 +385,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="204">Capabilities posted.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Sessions/Capabilities")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> PostCapabilities(
         [FromQuery] string? id,
@@ -419,7 +419,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="204">Capabilities updated.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Sessions/Capabilities/Full")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> PostFullCapabilities(
         [FromQuery] string? id,
@@ -443,7 +443,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="204">Session reported to server.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Sessions/Viewing")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> ReportViewing(
         [FromQuery] string? sessionId,
@@ -461,7 +461,7 @@ public class SessionController : BaseJellyfinApiController
     /// <response code="204">Session end reported to server.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Sessions/Logout")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> ReportSessionEnded()
     {

+ 1 - 2
Jellyfin.Api/Controllers/StudiosController.cs

@@ -1,6 +1,5 @@
 using System;
 using System.ComponentModel.DataAnnotations;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
@@ -21,7 +20,7 @@ namespace Jellyfin.Api.Controllers;
 /// <summary>
 /// Studios controller.
 /// </summary>
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class StudiosController : BaseJellyfinApiController
 {
     private readonly ILibraryManager _libraryManager;

+ 6 - 6
Jellyfin.Api/Controllers/SubtitleController.cs

@@ -114,7 +114,7 @@ public class SubtitleController : BaseJellyfinApiController
     /// <response code="200">Subtitles retrieved.</response>
     /// <returns>An array of <see cref="RemoteSubtitleInfo"/>.</returns>
     [HttpGet("Items/{itemId}/RemoteSearch/Subtitles/{language}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public async Task<ActionResult<IEnumerable<RemoteSubtitleInfo>>> SearchRemoteSubtitles(
         [FromRoute, Required] Guid itemId,
@@ -134,7 +134,7 @@ public class SubtitleController : BaseJellyfinApiController
     /// <response code="204">Subtitle downloaded.</response>
     /// <returns>A <see cref="NoContentResult"/>.</returns>
     [HttpPost("Items/{itemId}/RemoteSearch/Subtitles/{subtitleId}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public async Task<ActionResult> DownloadRemoteSubtitles(
         [FromRoute, Required] Guid itemId,
@@ -164,7 +164,7 @@ public class SubtitleController : BaseJellyfinApiController
     /// <response code="200">File returned.</response>
     /// <returns>A <see cref="FileStreamResult"/> with the subtitle file.</returns>
     [HttpGet("Providers/Subtitles/Subtitles/{id}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [Produces(MediaTypeNames.Application.Octet)]
     [ProducesFile("text/*")]
@@ -322,7 +322,7 @@ public class SubtitleController : BaseJellyfinApiController
     /// <response code="200">Subtitle playlist retrieved.</response>
     /// <returns>A <see cref="FileContentResult"/> with the HLS subtitle playlist.</returns>
     [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/subtitles.m3u8")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesPlaylistFile]
     [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
@@ -463,7 +463,7 @@ public class SubtitleController : BaseJellyfinApiController
     /// <response code="200">Information retrieved.</response>
     /// <returns>An array of <see cref="FontFile"/> with the available font files.</returns>
     [HttpGet("FallbackFont/Fonts")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public IEnumerable<FontFile> GetFallbackFontList()
     {
@@ -514,7 +514,7 @@ public class SubtitleController : BaseJellyfinApiController
     /// <response code="200">Fallback font file retrieved.</response>
     /// <returns>The fallback font file.</returns>
     [HttpGet("FallbackFont/Fonts/{name}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesFile("font/*")]
     public ActionResult GetFallbackFont([FromRoute, Required] string name)

+ 1 - 2
Jellyfin.Api/Controllers/SuggestionsController.cs

@@ -1,6 +1,5 @@
 using System;
 using System.ComponentModel.DataAnnotations;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Enums;
@@ -19,7 +18,7 @@ namespace Jellyfin.Api.Controllers;
 /// The suggestions controller.
 /// </summary>
 [Route("")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class SuggestionsController : BaseJellyfinApiController
 {
     private readonly IDtoService _dtoService;

+ 2 - 2
Jellyfin.Api/Controllers/SystemController.cs

@@ -172,7 +172,7 @@ public class SystemController : BaseJellyfinApiController
     /// <response code="200">Information retrieved.</response>
     /// <returns><see cref="EndPointInfo"/> with information about the endpoint.</returns>
     [HttpGet("Endpoint")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public ActionResult<EndPointInfo> GetEndpointInfo()
     {
@@ -210,7 +210,7 @@ public class SystemController : BaseJellyfinApiController
     /// <response code="200">Information retrieved.</response>
     /// <returns>An <see cref="IEnumerable{WakeOnLanInfo}"/> with the WakeOnLan infos.</returns>
     [HttpGet("WakeOnLanInfo")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [Obsolete("This endpoint is obsolete.")]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public ActionResult<IEnumerable<WakeOnLanInfo>> GetWakeOnLanInfo()

+ 1 - 2
Jellyfin.Api/Controllers/TrailersController.cs

@@ -1,5 +1,4 @@
 using System;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Enums;
 using MediaBrowser.Model.Dto;
@@ -14,7 +13,7 @@ namespace Jellyfin.Api.Controllers;
 /// <summary>
 /// The trailers controller.
 /// </summary>
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class TrailersController : BaseJellyfinApiController
 {
     private readonly ItemsController _itemsController;

+ 1 - 2
Jellyfin.Api/Controllers/TvShowsController.cs

@@ -2,7 +2,6 @@ using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Enums;
@@ -25,7 +24,7 @@ namespace Jellyfin.Api.Controllers;
 /// The tv shows controller.
 /// </summary>
 [Route("Shows")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class TvShowsController : BaseJellyfinApiController
 {
     private readonly IUserManager _userManager;

+ 1 - 2
Jellyfin.Api/Controllers/UniversalAudioController.cs

@@ -5,7 +5,6 @@ using System.Globalization;
 using System.Linq;
 using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
@@ -82,7 +81,7 @@ public class UniversalAudioController : BaseJellyfinApiController
     /// <returns>A <see cref="Task"/> containing the audio file.</returns>
     [HttpGet("Audio/{itemId}/universal")]
     [HttpHead("Audio/{itemId}/universal", Name = "HeadUniversalAudioStream")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status302Found)]
     [ProducesAudioFile]

+ 6 - 6
Jellyfin.Api/Controllers/UserController.cs

@@ -81,7 +81,7 @@ public class UserController : BaseJellyfinApiController
     /// <response code="200">Users returned.</response>
     /// <returns>An <see cref="IEnumerable{UserDto}"/> containing the users.</returns>
     [HttpGet]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public ActionResult<IEnumerable<UserDto>> GetUsers(
         [FromQuery] bool? isHidden,
@@ -251,7 +251,7 @@ public class UserController : BaseJellyfinApiController
     /// <response code="404">User not found.</response>
     /// <returns>A <see cref="NoContentResult"/> indicating success or a <see cref="ForbidResult"/> or a <see cref="NotFoundResult"/> on failure.</returns>
     [HttpPost("{userId}/Password")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -312,7 +312,7 @@ public class UserController : BaseJellyfinApiController
     /// <response code="404">User not found.</response>
     /// <returns>A <see cref="NoContentResult"/> indicating success or a <see cref="ForbidResult"/> or a <see cref="NotFoundResult"/> on failure.</returns>
     [HttpPost("{userId}/EasyPassword")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -354,7 +354,7 @@ public class UserController : BaseJellyfinApiController
     /// <response code="403">User update forbidden.</response>
     /// <returns>A <see cref="NoContentResult"/> indicating success or a <see cref="BadRequestResult"/> or a <see cref="ForbidResult"/> on failure.</returns>
     [HttpPost("{userId}")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     [ProducesResponseType(StatusCodes.Status400BadRequest)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
@@ -440,7 +440,7 @@ public class UserController : BaseJellyfinApiController
     /// <response code="403">User configuration update forbidden.</response>
     /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
     [HttpPost("{userId}/Configuration")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     public async Task<ActionResult> UpdateUserConfiguration(
@@ -526,7 +526,7 @@ public class UserController : BaseJellyfinApiController
     /// <response code="400">Token is not owned by a user.</response>
     /// <returns>A <see cref="UserDto"/> for the authenticated user.</returns>
     [HttpGet("Me")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     [ProducesResponseType(StatusCodes.Status400BadRequest)]
     public ActionResult<UserDto> GetCurrentUser()

+ 1 - 2
Jellyfin.Api/Controllers/UserLibraryController.cs

@@ -4,7 +4,6 @@ using System.ComponentModel.DataAnnotations;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Enums;
@@ -28,7 +27,7 @@ namespace Jellyfin.Api.Controllers;
 /// User library controller.
 /// </summary>
 [Route("")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class UserLibraryController : BaseJellyfinApiController
 {
     private readonly IUserManager _userManager;

+ 1 - 2
Jellyfin.Api/Controllers/UserViewsController.cs

@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 using System.Globalization;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Api.Models.UserViewDtos;
@@ -23,7 +22,7 @@ namespace Jellyfin.Api.Controllers;
 /// User views controller.
 /// </summary>
 [Route("")]
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class UserViewsController : BaseJellyfinApiController
 {
     private readonly IUserManager _userManager;

+ 1 - 1
Jellyfin.Api/Controllers/VideosController.cs

@@ -100,7 +100,7 @@ public class VideosController : BaseJellyfinApiController
     /// <response code="200">Additional parts returned.</response>
     /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the parts.</returns>
     [HttpGet("{itemId}/AdditionalParts")]
-    [Authorize(Policy = Policies.DefaultAuthorization)]
+    [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public ActionResult<QueryResult<BaseItemDto>> GetAdditionalPart([FromRoute, Required] Guid itemId, [FromQuery] Guid? userId)
     {

+ 1 - 2
Jellyfin.Api/Controllers/YearsController.cs

@@ -2,7 +2,6 @@ using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
 using System.Linq;
-using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
@@ -24,7 +23,7 @@ namespace Jellyfin.Api.Controllers;
 /// <summary>
 /// Years controller.
 /// </summary>
-[Authorize(Policy = Policies.DefaultAuthorization)]
+[Authorize]
 public class YearsController : BaseJellyfinApiController
 {
     private readonly ILibraryManager _libraryManager;

+ 8 - 3
Jellyfin.Api/Middleware/LanFilteringMiddleware.cs

@@ -1,6 +1,6 @@
-using System.Net;
 using System.Threading.Tasks;
 using Jellyfin.Networking.Configuration;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using Microsoft.AspNetCore.Http;
@@ -32,9 +32,14 @@ public class LanFilteringMiddleware
     /// <returns>The async task.</returns>
     public async Task Invoke(HttpContext httpContext, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager)
     {
-        var host = httpContext.Connection.RemoteIpAddress ?? IPAddress.Loopback;
+        if (serverConfigurationManager.GetNetworkConfiguration().EnableRemoteAccess)
+        {
+            await _next(httpContext).ConfigureAwait(false);
+            return;
+        }
 
-        if (!networkManager.IsInLocalNetwork(host) && !serverConfigurationManager.GetNetworkConfiguration().EnableRemoteAccess)
+        var host = httpContext.GetNormalizedRemoteIp();
+        if (!networkManager.IsInLocalNetwork(host))
         {
             return;
         }

+ 11 - 0
Jellyfin.Data/DayOfWeekHelper.cs

@@ -17,5 +17,16 @@ namespace Jellyfin.Data
                 _ => new[] { (DayOfWeek)day }
             };
         }
+
+        public static bool Contains(this DynamicDayOfWeek dynamicDayOfWeek, DayOfWeek dayOfWeek)
+        {
+            return dynamicDayOfWeek switch
+            {
+                DynamicDayOfWeek.Everyday => true,
+                DynamicDayOfWeek.Weekday => dayOfWeek is > DayOfWeek.Sunday and <= DayOfWeek.Friday,
+                DynamicDayOfWeek.Weekend => dayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday,
+                _ => (DayOfWeek)dynamicDayOfWeek == dayOfWeek
+            };
+        }
     }
 }

+ 2 - 1
Jellyfin.Data/Entities/User.cs

@@ -525,8 +525,9 @@ namespace Jellyfin.Data.Entities
         {
             var localTime = date.ToLocalTime();
             var hour = localTime.TimeOfDay.TotalHours;
+            var currentDayOfWeek = localTime.DayOfWeek;
 
-            return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek)
+            return schedule.DayOfWeek.Contains(currentDayOfWeek)
                    && hour >= schedule.StartHour
                    && hour <= schedule.EndHour;
         }

+ 32 - 111
Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs

@@ -5,19 +5,15 @@ using System.Linq;
 using System.Net;
 using System.Net.Sockets;
 using System.Reflection;
+using System.Security.Claims;
 using Emby.Server.Implementations;
 using Jellyfin.Api.Auth;
 using Jellyfin.Api.Auth.AnonymousLanAccessPolicy;
 using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
 using Jellyfin.Api.Auth.DownloadPolicy;
-using Jellyfin.Api.Auth.FirstTimeOrIgnoreParentalControlSetupPolicy;
-using Jellyfin.Api.Auth.FirstTimeSetupOrDefaultPolicy;
-using Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy;
-using Jellyfin.Api.Auth.IgnoreParentalControlPolicy;
-using Jellyfin.Api.Auth.LocalAccessOrRequiresElevationPolicy;
-using Jellyfin.Api.Auth.LocalAccessPolicy;
-using Jellyfin.Api.Auth.RequiresElevationPolicy;
+using Jellyfin.Api.Auth.FirstTimeSetupPolicy;
 using Jellyfin.Api.Auth.SyncPlayAccessPolicy;
+using Jellyfin.Api.Auth.UserPermissionPolicy;
 using Jellyfin.Api.Constants;
 using Jellyfin.Api.Controllers;
 using Jellyfin.Api.Formatters;
@@ -56,117 +52,34 @@ namespace Jellyfin.Server.Extensions
         /// <returns>The updated service collection.</returns>
         public static IServiceCollection AddJellyfinApiAuthorization(this IServiceCollection serviceCollection)
         {
+            // The default handler must be first so that it is evaluated first
             serviceCollection.AddSingleton<IAuthorizationHandler, DefaultAuthorizationHandler>();
-            serviceCollection.AddSingleton<IAuthorizationHandler, DownloadHandler>();
-            serviceCollection.AddSingleton<IAuthorizationHandler, FirstTimeSetupOrDefaultHandler>();
-            serviceCollection.AddSingleton<IAuthorizationHandler, FirstTimeSetupOrElevatedHandler>();
-            serviceCollection.AddSingleton<IAuthorizationHandler, IgnoreParentalControlHandler>();
-            serviceCollection.AddSingleton<IAuthorizationHandler, FirstTimeOrIgnoreParentalControlSetupHandler>();
-            serviceCollection.AddSingleton<IAuthorizationHandler, LocalAccessHandler>();
+            serviceCollection.AddSingleton<IAuthorizationHandler, UserPermissionHandler>();
+            serviceCollection.AddSingleton<IAuthorizationHandler, FirstTimeSetupHandler>();
             serviceCollection.AddSingleton<IAuthorizationHandler, AnonymousLanAccessHandler>();
-            serviceCollection.AddSingleton<IAuthorizationHandler, LocalAccessOrRequiresElevationHandler>();
-            serviceCollection.AddSingleton<IAuthorizationHandler, RequiresElevationHandler>();
             serviceCollection.AddSingleton<IAuthorizationHandler, SyncPlayAccessHandler>();
+
             return serviceCollection.AddAuthorizationCore(options =>
             {
-                options.AddPolicy(
-                    Policies.DefaultAuthorization,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new DefaultAuthorizationRequirement());
-                    });
-                options.AddPolicy(
-                    Policies.Download,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new DownloadRequirement());
-                    });
-                options.AddPolicy(
-                    Policies.FirstTimeSetupOrDefault,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new FirstTimeSetupOrDefaultRequirement());
-                    });
-                options.AddPolicy(
-                    Policies.FirstTimeSetupOrElevated,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new FirstTimeSetupOrElevatedRequirement());
-                    });
-                options.AddPolicy(
-                    Policies.IgnoreParentalControl,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new IgnoreParentalControlRequirement());
-                    });
-                options.AddPolicy(
-                    Policies.FirstTimeSetupOrIgnoreParentalControl,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new FirstTimeOrIgnoreParentalControlSetupRequirement());
-                    });
-                options.AddPolicy(
-                    Policies.LocalAccessOnly,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new LocalAccessRequirement());
-                    });
-                options.AddPolicy(
-                    Policies.LocalAccessOrRequiresElevation,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new LocalAccessOrRequiresElevationRequirement());
-                    });
+                options.DefaultPolicy = new AuthorizationPolicyBuilder()
+                    .AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication)
+                    .RequireAuthenticatedUser()
+                    .Build();
+
+                options.AddPolicy(Policies.Download, new UserPermissionRequirement(PermissionKind.EnableContentDownloading));
+                options.AddPolicy(Policies.FirstTimeSetupOrDefault, new FirstTimeSetupRequirement());
+                options.AddPolicy(Policies.FirstTimeSetupOrElevated, new FirstTimeSetupRequirement(requireAdmin: true));
+                options.AddPolicy(Policies.FirstTimeSetupOrIgnoreParentalControl, new FirstTimeSetupRequirement(validateParentalSchedule: false));
+                options.AddPolicy(Policies.IgnoreParentalControl, new DefaultAuthorizationRequirement(validateParentalSchedule: false));
+                options.AddPolicy(Policies.SyncPlayHasAccess, new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.HasAccess));
+                options.AddPolicy(Policies.SyncPlayCreateGroup, new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.CreateGroup));
+                options.AddPolicy(Policies.SyncPlayJoinGroup, new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.JoinGroup));
+                options.AddPolicy(Policies.SyncPlayIsInGroup, new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.IsInGroup));
+                options.AddPolicy(Policies.AnonymousLanAccessPolicy, new AnonymousLanAccessRequirement());
                 options.AddPolicy(
                     Policies.RequiresElevation,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new RequiresElevationRequirement());
-                    });
-                options.AddPolicy(
-                    Policies.SyncPlayHasAccess,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.HasAccess));
-                    });
-                options.AddPolicy(
-                    Policies.SyncPlayCreateGroup,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.CreateGroup));
-                    });
-                options.AddPolicy(
-                    Policies.SyncPlayJoinGroup,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.JoinGroup));
-                    });
-                options.AddPolicy(
-                    Policies.SyncPlayIsInGroup,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.IsInGroup));
-                    });
-                options.AddPolicy(
-                    Policies.AnonymousLanAccessPolicy,
-                    policy =>
-                    {
-                        policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
-                        policy.AddRequirements(new AnonymousLanAccessRequirement());
-                    });
+                    policy => policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication)
+                        .RequireClaim(ClaimTypes.Role, UserRoles.Administrator));
             });
         }
 
@@ -334,6 +247,14 @@ namespace Jellyfin.Server.Extensions
             });
         }
 
+        private static void AddPolicy(this AuthorizationOptions authorizationOptions, string policyName, IAuthorizationRequirement authorizationRequirement)
+        {
+            authorizationOptions.AddPolicy(policyName, policy =>
+            {
+                policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication).AddRequirements(authorizationRequirement);
+            });
+        }
+
         /// <summary>
         /// Sets up the proxy configuration based on the addresses in <paramref name="allowedProxies"/>.
         /// </summary>

+ 10 - 9
tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs → tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs

@@ -2,7 +2,8 @@ using System.Collections.Generic;
 using System.Threading.Tasks;
 using AutoFixture;
 using AutoFixture.AutoMoq;
-using Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy;
+using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
+using Jellyfin.Api.Auth.FirstTimeSetupPolicy;
 using Jellyfin.Api.Constants;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Library;
@@ -11,25 +12,25 @@ using Microsoft.AspNetCore.Http;
 using Moq;
 using Xunit;
 
-namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy
+namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupPolicy
 {
-    public class FirstTimeSetupOrElevatedHandlerTests
+    public class FirstTimeSetupHandlerTests
     {
         private readonly Mock<IConfigurationManager> _configurationManagerMock;
         private readonly List<IAuthorizationRequirement> _requirements;
-        private readonly FirstTimeSetupOrElevatedHandler _sut;
+        private readonly FirstTimeSetupHandler _firstTimeSetupHandler;
         private readonly Mock<IUserManager> _userManagerMock;
         private readonly Mock<IHttpContextAccessor> _httpContextAccessor;
 
-        public FirstTimeSetupOrElevatedHandlerTests()
+        public FirstTimeSetupHandlerTests()
         {
             var fixture = new Fixture().Customize(new AutoMoqCustomization());
             _configurationManagerMock = fixture.Freeze<Mock<IConfigurationManager>>();
-            _requirements = new List<IAuthorizationRequirement> { new FirstTimeSetupOrElevatedRequirement() };
+            _requirements = new List<IAuthorizationRequirement> { new FirstTimeSetupRequirement() };
             _userManagerMock = fixture.Freeze<Mock<IUserManager>>();
             _httpContextAccessor = fixture.Freeze<Mock<IHttpContextAccessor>>();
 
-            _sut = fixture.Create<FirstTimeSetupOrElevatedHandler>();
+            _firstTimeSetupHandler = fixture.Create<FirstTimeSetupHandler>();
         }
 
         [Theory]
@@ -46,7 +47,7 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy
 
             var context = new AuthorizationHandlerContext(_requirements, claims, null);
 
-            await _sut.HandleAsync(context);
+            await _firstTimeSetupHandler.HandleAsync(context);
             Assert.True(context.HasSucceeded);
         }
 
@@ -64,7 +65,7 @@ namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy
 
             var context = new AuthorizationHandlerContext(_requirements, claims, null);
 
-            await _sut.HandleAsync(context);
+            await _firstTimeSetupHandler.HandleAsync(context);
             Assert.Equal(shouldSucceed, context.HasSucceeded);
         }
     }

+ 4 - 4
tests/Jellyfin.Api.Tests/Auth/IgnoreSchedulePolicy/IgnoreScheduleHandlerTests.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 using System.Threading.Tasks;
 using AutoFixture;
 using AutoFixture.AutoMoq;
-using Jellyfin.Api.Auth.IgnoreParentalControlPolicy;
+using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
 using Jellyfin.Api.Constants;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
@@ -20,7 +20,7 @@ namespace Jellyfin.Api.Tests.Auth.IgnoreSchedulePolicy
     {
         private readonly Mock<IConfigurationManager> _configurationManagerMock;
         private readonly List<IAuthorizationRequirement> _requirements;
-        private readonly IgnoreParentalControlHandler _sut;
+        private readonly DefaultAuthorizationHandler _sut;
         private readonly Mock<IUserManager> _userManagerMock;
         private readonly Mock<IHttpContextAccessor> _httpContextAccessor;
 
@@ -33,11 +33,11 @@ namespace Jellyfin.Api.Tests.Auth.IgnoreSchedulePolicy
         {
             var fixture = new Fixture().Customize(new AutoMoqCustomization());
             _configurationManagerMock = fixture.Freeze<Mock<IConfigurationManager>>();
-            _requirements = new List<IAuthorizationRequirement> { new IgnoreParentalControlRequirement() };
+            _requirements = new List<IAuthorizationRequirement> { new DefaultAuthorizationRequirement(validateParentalSchedule: false) };
             _userManagerMock = fixture.Freeze<Mock<IUserManager>>();
             _httpContextAccessor = fixture.Freeze<Mock<IHttpContextAccessor>>();
 
-            _sut = fixture.Create<IgnoreParentalControlHandler>();
+            _sut = fixture.Create<DefaultAuthorizationHandler>();
         }
 
         [Theory]

+ 0 - 59
tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs

@@ -1,59 +0,0 @@
-using System.Collections.Generic;
-using System.Net;
-using System.Threading.Tasks;
-using AutoFixture;
-using AutoFixture.AutoMoq;
-using Jellyfin.Api.Auth.LocalAccessPolicy;
-using Jellyfin.Api.Constants;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Library;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Moq;
-using Xunit;
-
-namespace Jellyfin.Api.Tests.Auth.LocalAccessPolicy
-{
-    public class LocalAccessHandlerTests
-    {
-        private readonly Mock<IConfigurationManager> _configurationManagerMock;
-        private readonly List<IAuthorizationRequirement> _requirements;
-        private readonly LocalAccessHandler _sut;
-        private readonly Mock<IUserManager> _userManagerMock;
-        private readonly Mock<IHttpContextAccessor> _httpContextAccessor;
-        private readonly Mock<INetworkManager> _networkManagerMock;
-
-        public LocalAccessHandlerTests()
-        {
-            var fixture = new Fixture().Customize(new AutoMoqCustomization());
-            _configurationManagerMock = fixture.Freeze<Mock<IConfigurationManager>>();
-            _requirements = new List<IAuthorizationRequirement> { new LocalAccessRequirement() };
-            _userManagerMock = fixture.Freeze<Mock<IUserManager>>();
-            _httpContextAccessor = fixture.Freeze<Mock<IHttpContextAccessor>>();
-            _networkManagerMock = fixture.Freeze<Mock<INetworkManager>>();
-
-            _sut = fixture.Create<LocalAccessHandler>();
-        }
-
-        [Theory]
-        [InlineData(true, true)]
-        [InlineData(false, false)]
-        public async Task LocalAccessOnly(bool isInLocalNetwork, bool shouldSucceed)
-        {
-            _networkManagerMock
-                .Setup(n => n.IsInLocalNetwork(It.IsAny<IPAddress>()))
-                .Returns(isInLocalNetwork);
-
-            TestHelpers.SetupConfigurationManager(_configurationManagerMock, true);
-            var claims = TestHelpers.SetupUser(
-                _userManagerMock,
-                _httpContextAccessor,
-                UserRoles.User);
-
-            var context = new AuthorizationHandlerContext(_requirements, claims, null);
-            await _sut.HandleAsync(context);
-            Assert.Equal(shouldSucceed, context.HasSucceeded);
-        }
-    }
-}

+ 0 - 53
tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs

@@ -1,53 +0,0 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using AutoFixture;
-using AutoFixture.AutoMoq;
-using Jellyfin.Api.Auth.RequiresElevationPolicy;
-using Jellyfin.Api.Constants;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Library;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Moq;
-using Xunit;
-
-namespace Jellyfin.Api.Tests.Auth.RequiresElevationPolicy
-{
-    public class RequiresElevationHandlerTests
-    {
-        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()
-        {
-            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]
-        [InlineData(UserRoles.Administrator, true)]
-        [InlineData(UserRoles.User, false)]
-        [InlineData(UserRoles.Guest, false)]
-        public async Task ShouldHandleRolesCorrectly(string role, bool shouldSucceed)
-        {
-            TestHelpers.SetupConfigurationManager(_configurationManagerMock, true);
-            var claims = TestHelpers.SetupUser(
-                _userManagerMock,
-                _httpContextAccessor,
-                role);
-
-            var context = new AuthorizationHandlerContext(_requirements, claims, null);
-
-            await _sut.HandleAsync(context);
-            Assert.Equal(shouldSucceed, context.HasSucceeded);
-        }
-    }
-}