From 209edd38a4163a8cf4abd5e47bfe0ea1a100f351 Mon Sep 17 00:00:00 2001 From: cvium Date: Wed, 8 Feb 2023 23:55:26 +0100 Subject: [PATCH] refactor: simplify authz --- .../AnonymousLanAccessHandler.cs | 3 +- Jellyfin.Api/Auth/BaseAuthorizationHandler.cs | 113 -------------- .../DefaultAuthorizationHandler.cs | 47 +++++- .../DefaultAuthorizationRequirement.cs | 13 ++ .../Auth/DownloadPolicy/DownloadHandler.cs | 44 ------ .../DownloadPolicy/DownloadRequirement.cs | 11 -- ...TimeOrIgnoreParentalControlSetupHandler.cs | 56 ------- ...OrIgnoreParentalControlSetupRequirement.cs | 11 -- .../FirstTimeSetupOrDefaultRequirement.cs | 11 -- .../FirstTimeSetupOrElevatedHandler.cs | 57 ------- .../FirstTimeSetupOrElevatedRequirement.cs | 11 -- .../FirstTimeSetupHandler.cs} | 39 ++--- .../FirstTimeSetupRequirement.cs | 26 ++++ .../IgnoreParentalControlHandler.cs | 44 ------ .../IgnoreParentalControlRequirement.cs | 11 -- .../LocalAccessOrRequiresElevationHandler.cs | 45 ------ ...calAccessOrRequiresElevationRequirement.cs | 11 -- .../LocalAccessPolicy/LocalAccessHandler.cs | 44 ------ .../LocalAccessRequirement.cs | 11 -- .../RequiresElevationHandler.cs | 45 ------ .../RequiresElevationRequirement.cs | 11 -- .../SyncPlayAccessHandler.cs | 41 +---- .../SyncPlayAccessRequirement.cs | 6 +- .../UserPermissionHandler.cs | 37 +++++ .../UserPermissionRequirement.cs | 26 ++++ Jellyfin.Api/Constants/Policies.cs | 5 - Jellyfin.Api/Controllers/ArtistsController.cs | 3 +- .../Controllers/ChannelsController.cs | 3 +- .../Controllers/ClientLogController.cs | 3 +- .../Controllers/CollectionController.cs | 3 +- .../Controllers/ConfigurationController.cs | 2 +- .../Controllers/DashboardController.cs | 3 +- .../DisplayPreferencesController.cs | 3 +- .../Controllers/DynamicHlsController.cs | 3 +- Jellyfin.Api/Controllers/FilterController.cs | 3 +- Jellyfin.Api/Controllers/GenresController.cs | 5 +- .../Controllers/HlsSegmentController.cs | 5 +- Jellyfin.Api/Controllers/ImageController.cs | 18 +-- .../Controllers/InstantMixController.cs | 3 +- .../Controllers/ItemLookupController.cs | 2 +- Jellyfin.Api/Controllers/ItemsController.cs | 3 +- Jellyfin.Api/Controllers/LibraryController.cs | 80 +++++----- Jellyfin.Api/Controllers/LiveTvController.cs | 77 +++++----- .../Controllers/MediaInfoController.cs | 3 +- Jellyfin.Api/Controllers/MoviesController.cs | 3 +- .../Controllers/MusicGenresController.cs | 3 +- Jellyfin.Api/Controllers/PackageController.cs | 2 +- Jellyfin.Api/Controllers/PersonsController.cs | 3 +- .../Controllers/PlaylistsController.cs | 3 +- .../Controllers/PlaystateController.cs | 3 +- Jellyfin.Api/Controllers/PluginsController.cs | 2 +- .../Controllers/QuickConnectController.cs | 2 +- .../Controllers/RemoteImageController.cs | 4 +- Jellyfin.Api/Controllers/SearchController.cs | 3 +- Jellyfin.Api/Controllers/SessionController.cs | 28 ++-- Jellyfin.Api/Controllers/StudiosController.cs | 3 +- .../Controllers/SubtitleController.cs | 12 +- .../Controllers/SuggestionsController.cs | 3 +- Jellyfin.Api/Controllers/SystemController.cs | 4 +- .../Controllers/TrailersController.cs | 3 +- Jellyfin.Api/Controllers/TvShowsController.cs | 3 +- .../Controllers/UniversalAudioController.cs | 3 +- Jellyfin.Api/Controllers/UserController.cs | 12 +- .../Controllers/UserLibraryController.cs | 3 +- .../Controllers/UserViewsController.cs | 3 +- Jellyfin.Api/Controllers/VideosController.cs | 2 +- Jellyfin.Api/Controllers/YearsController.cs | 3 +- .../Middleware/LanFilteringMiddleware.cs | 11 +- Jellyfin.Data/DayOfWeekHelper.cs | 11 ++ Jellyfin.Data/Entities/User.cs | 3 +- .../ApiServiceCollectionExtensions.cs | 143 ++++-------------- .../FirstTimeSetupHandlerTests.cs} | 19 +-- .../IgnoreScheduleHandlerTests.cs | 8 +- .../LocalAccessHandlerTests.cs | 59 -------- .../RequiresElevationHandlerTests.cs | 53 ------- 75 files changed, 393 insertions(+), 1025 deletions(-) delete mode 100644 Jellyfin.Api/Auth/BaseAuthorizationHandler.cs delete mode 100644 Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs delete mode 100644 Jellyfin.Api/Auth/DownloadPolicy/DownloadRequirement.cs delete mode 100644 Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs delete mode 100644 Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupRequirement.cs delete mode 100644 Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultRequirement.cs delete mode 100644 Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs delete mode 100644 Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedRequirement.cs rename Jellyfin.Api/Auth/{FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs => FirstTimeSetupPolicy/FirstTimeSetupHandler.cs} (58%) create mode 100644 Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupRequirement.cs delete mode 100644 Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs delete mode 100644 Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlRequirement.cs delete mode 100644 Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs delete mode 100644 Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationRequirement.cs delete mode 100644 Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs delete mode 100644 Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessRequirement.cs delete mode 100644 Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs delete mode 100644 Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationRequirement.cs create mode 100644 Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs create mode 100644 Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs rename tests/Jellyfin.Api.Tests/Auth/{FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs => FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs} (80%) delete mode 100644 tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs delete mode 100644 tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs diff --git a/Jellyfin.Api/Auth/AnonymousLanAccessPolicy/AnonymousLanAccessHandler.cs b/Jellyfin.Api/Auth/AnonymousLanAccessPolicy/AnonymousLanAccessHandler.cs index d4b1ffb060..741b88ea95 100644 --- a/Jellyfin.Api/Auth/AnonymousLanAccessPolicy/AnonymousLanAccessHandler.cs +++ b/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 /// 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)) diff --git a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs deleted file mode 100644 index 8e5e66d64a..0000000000 --- a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs +++ /dev/null @@ -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 -{ - /// - /// Base authorization handler. - /// - /// Type of Authorization Requirement. - public abstract class BaseAuthorizationHandler : AuthorizationHandler - where T : IAuthorizationRequirement - { - private readonly IUserManager _userManager; - private readonly INetworkManager _networkManager; - private readonly IHttpContextAccessor _httpContextAccessor; - - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - protected BaseAuthorizationHandler( - IUserManager userManager, - INetworkManager networkManager, - IHttpContextAccessor httpContextAccessor) - { - _userManager = userManager; - _networkManager = networkManager; - _httpContextAccessor = httpContextAccessor; - } - - /// - /// Validate authenticated claims. - /// - /// Request claims. - /// Whether to ignore parental control. - /// Whether access is to be allowed locally only. - /// Whether validation requires download permission. - /// Validated claim status. - 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; - } - } -} diff --git a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs index be77b7a4e4..7489e2a35c 100644 --- a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs +++ b/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 /// /// Default authorization handler. /// - public class DefaultAuthorizationHandler : BaseAuthorizationHandler + public class DefaultAuthorizationHandler : AuthorizationHandler { + private readonly IUserManager _userManager; + private readonly INetworkManager _networkManager; + private readonly IHttpContextAccessor _httpContextAccessor; + /// /// Initializes a new instance of the class. /// @@ -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; } /// 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; diff --git a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationRequirement.cs b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationRequirement.cs index 7cea00b694..0846e7515a 100644 --- a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationRequirement.cs +++ b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationRequirement.cs @@ -7,5 +7,18 @@ namespace Jellyfin.Api.Auth.DefaultAuthorizationPolicy /// public class DefaultAuthorizationRequirement : IAuthorizationRequirement { + /// + /// Initializes a new instance of the class. + /// + /// A value indicating whether to validate parental schedule. + public DefaultAuthorizationRequirement(bool validateParentalSchedule = true) + { + ValidateParentalSchedule = validateParentalSchedule; + } + + /// + /// Gets a value indicating whether to ignore parental schedule. + /// + public bool ValidateParentalSchedule { get; init; } } } diff --git a/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs b/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs deleted file mode 100644 index b61680ab1a..0000000000 --- a/Jellyfin.Api/Auth/DownloadPolicy/DownloadHandler.cs +++ /dev/null @@ -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 -{ - /// - /// Download authorization handler. - /// - public class DownloadHandler : BaseAuthorizationHandler - { - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - public DownloadHandler( - IUserManager userManager, - INetworkManager networkManager, - IHttpContextAccessor httpContextAccessor) - : base(userManager, networkManager, httpContextAccessor) - { - } - - /// - protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DownloadRequirement requirement) - { - var validated = ValidateClaims(context.User); - if (validated) - { - context.Succeed(requirement); - } - else - { - context.Fail(); - } - - return Task.CompletedTask; - } - } -} diff --git a/Jellyfin.Api/Auth/DownloadPolicy/DownloadRequirement.cs b/Jellyfin.Api/Auth/DownloadPolicy/DownloadRequirement.cs deleted file mode 100644 index b0a72a9dec..0000000000 --- a/Jellyfin.Api/Auth/DownloadPolicy/DownloadRequirement.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.AspNetCore.Authorization; - -namespace Jellyfin.Api.Auth.DownloadPolicy -{ - /// - /// The download permission requirement. - /// - public class DownloadRequirement : IAuthorizationRequirement - { - } -} diff --git a/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs b/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs deleted file mode 100644 index 31482a930f..0000000000 --- a/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupHandler.cs +++ /dev/null @@ -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 -{ - /// - /// Ignore parental control schedule and allow before startup wizard has been completed. - /// - public class FirstTimeOrIgnoreParentalControlSetupHandler : BaseAuthorizationHandler - { - private readonly IConfigurationManager _configurationManager; - - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - public FirstTimeOrIgnoreParentalControlSetupHandler( - IUserManager userManager, - INetworkManager networkManager, - IHttpContextAccessor httpContextAccessor, - IConfigurationManager configurationManager) - : base(userManager, networkManager, httpContextAccessor) - { - _configurationManager = configurationManager; - } - - /// - 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; - } - } -} diff --git a/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupRequirement.cs b/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupRequirement.cs deleted file mode 100644 index 00aaec334b..0000000000 --- a/Jellyfin.Api/Auth/FirstTimeOrIgnoreParentalControlSetupPolicy/FirstTimeOrIgnoreParentalControlSetupRequirement.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.AspNetCore.Authorization; - -namespace Jellyfin.Api.Auth.FirstTimeOrIgnoreParentalControlSetupPolicy -{ - /// - /// First time setup or ignore parental controls requirement. - /// - public class FirstTimeOrIgnoreParentalControlSetupRequirement : IAuthorizationRequirement - { - } -} diff --git a/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultRequirement.cs b/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultRequirement.cs deleted file mode 100644 index f7366bd7a9..0000000000 --- a/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultRequirement.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.AspNetCore.Authorization; - -namespace Jellyfin.Api.Auth.FirstTimeSetupOrDefaultPolicy -{ - /// - /// The authorization requirement, requiring incomplete first time setup or default privileges, for the authorization handler. - /// - public class FirstTimeSetupOrDefaultRequirement : IAuthorizationRequirement - { - } -} diff --git a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs deleted file mode 100644 index 90b76ee99a..0000000000 --- a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs +++ /dev/null @@ -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 -{ - /// - /// Authorization handler for requiring first time setup or elevated privileges. - /// - public class FirstTimeSetupOrElevatedHandler : BaseAuthorizationHandler - { - private readonly IConfigurationManager _configurationManager; - - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - public FirstTimeSetupOrElevatedHandler( - IConfigurationManager configurationManager, - IUserManager userManager, - INetworkManager networkManager, - IHttpContextAccessor httpContextAccessor) - : base(userManager, networkManager, httpContextAccessor) - { - _configurationManager = configurationManager; - } - - /// - 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; - } - } -} diff --git a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedRequirement.cs b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedRequirement.cs deleted file mode 100644 index 51ba637b60..0000000000 --- a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedRequirement.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.AspNetCore.Authorization; - -namespace Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy -{ - /// - /// The authorization requirement, requiring incomplete first time setup or elevated privileges, for the authorization handler. - /// - public class FirstTimeSetupOrElevatedRequirement : IAuthorizationRequirement - { - } -} diff --git a/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs similarity index 58% rename from Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs rename to Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs index dd0bd4ec2f..302e052a7c 100644 --- a/Jellyfin.Api/Auth/FirstTimeSetupOrDefaultPolicy/FirstTimeSetupOrDefaultHandler.cs +++ b/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 { /// /// Authorization handler for requiring first time setup or default privileges. /// - public class FirstTimeSetupOrDefaultHandler : BaseAuthorizationHandler + public class FirstTimeSetupHandler : AuthorizationHandler { private readonly IConfigurationManager _configurationManager; + private readonly IUserManager _userManager; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - public FirstTimeSetupOrDefaultHandler( + public FirstTimeSetupHandler( IConfigurationManager configurationManager, - IUserManager userManager, - INetworkManager networkManager, - IHttpContextAccessor httpContextAccessor) - : base(userManager, networkManager, httpContextAccessor) + IUserManager userManager) { _configurationManager = configurationManager; + _userManager = userManager; } /// - 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; diff --git a/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupRequirement.cs b/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupRequirement.cs new file mode 100644 index 0000000000..8b7a94954e --- /dev/null +++ b/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupRequirement.cs @@ -0,0 +1,26 @@ +using Jellyfin.Api.Auth.DefaultAuthorizationPolicy; + +namespace Jellyfin.Api.Auth.FirstTimeSetupPolicy +{ + /// + /// The authorization requirement, requiring incomplete first time setup or default privileges, for the authorization handler. + /// + public class FirstTimeSetupRequirement : DefaultAuthorizationRequirement + { + /// + /// Initializes a new instance of the class. + /// + /// A value indicating whether to ignore parental schedule. + /// A value indicating whether administrator role is required. + public FirstTimeSetupRequirement(bool validateParentalSchedule = false, bool requireAdmin = true) + { + ValidateParentalSchedule = validateParentalSchedule; + RequireAdmin = requireAdmin; + } + + /// + /// Gets a value indicating whether administrator role is required. + /// + public bool RequireAdmin { get; } + } +} diff --git a/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs b/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs deleted file mode 100644 index a7623556a9..0000000000 --- a/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlHandler.cs +++ /dev/null @@ -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 -{ - /// - /// Escape schedule controls handler. - /// - public class IgnoreParentalControlHandler : BaseAuthorizationHandler - { - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - public IgnoreParentalControlHandler( - IUserManager userManager, - INetworkManager networkManager, - IHttpContextAccessor httpContextAccessor) - : base(userManager, networkManager, httpContextAccessor) - { - } - - /// - 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; - } - } -} diff --git a/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlRequirement.cs b/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlRequirement.cs deleted file mode 100644 index cdad74270e..0000000000 --- a/Jellyfin.Api/Auth/IgnoreParentalControlPolicy/IgnoreParentalControlRequirement.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.AspNetCore.Authorization; - -namespace Jellyfin.Api.Auth.IgnoreParentalControlPolicy -{ - /// - /// Escape schedule controls requirement. - /// - public class IgnoreParentalControlRequirement : IAuthorizationRequirement - { - } -} diff --git a/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs b/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs deleted file mode 100644 index 14722aa57e..0000000000 --- a/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs +++ /dev/null @@ -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 -{ - /// - /// Local access or require elevated privileges handler. - /// - public class LocalAccessOrRequiresElevationHandler : BaseAuthorizationHandler - { - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - public LocalAccessOrRequiresElevationHandler( - IUserManager userManager, - INetworkManager networkManager, - IHttpContextAccessor httpContextAccessor) - : base(userManager, networkManager, httpContextAccessor) - { - } - - /// - 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; - } - } -} diff --git a/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationRequirement.cs b/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationRequirement.cs deleted file mode 100644 index d9c64d01c4..0000000000 --- a/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationRequirement.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.AspNetCore.Authorization; - -namespace Jellyfin.Api.Auth.LocalAccessOrRequiresElevationPolicy -{ - /// - /// The local access or elevated privileges authorization requirement. - /// - public class LocalAccessOrRequiresElevationRequirement : IAuthorizationRequirement - { - } -} diff --git a/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs b/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs deleted file mode 100644 index d772ec5542..0000000000 --- a/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessHandler.cs +++ /dev/null @@ -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 -{ - /// - /// Local access handler. - /// - public class LocalAccessHandler : BaseAuthorizationHandler - { - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - public LocalAccessHandler( - IUserManager userManager, - INetworkManager networkManager, - IHttpContextAccessor httpContextAccessor) - : base(userManager, networkManager, httpContextAccessor) - { - } - - /// - 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; - } - } -} diff --git a/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessRequirement.cs b/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessRequirement.cs deleted file mode 100644 index 761127fa40..0000000000 --- a/Jellyfin.Api/Auth/LocalAccessPolicy/LocalAccessRequirement.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.AspNetCore.Authorization; - -namespace Jellyfin.Api.Auth.LocalAccessPolicy -{ - /// - /// The local access authorization requirement. - /// - public class LocalAccessRequirement : IAuthorizationRequirement - { - } -} diff --git a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs b/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs deleted file mode 100644 index b235c4b63b..0000000000 --- a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs +++ /dev/null @@ -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 -{ - /// - /// Authorization handler for requiring elevated privileges. - /// - public class RequiresElevationHandler : BaseAuthorizationHandler - { - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. - public RequiresElevationHandler( - IUserManager userManager, - INetworkManager networkManager, - IHttpContextAccessor httpContextAccessor) - : base(userManager, networkManager, httpContextAccessor) - { - } - - /// - 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; - } - } -} diff --git a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationRequirement.cs b/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationRequirement.cs deleted file mode 100644 index cfff1cc0c5..0000000000 --- a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationRequirement.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.AspNetCore.Authorization; - -namespace Jellyfin.Api.Auth.RequiresElevationPolicy -{ - /// - /// The authorization requirement for requiring elevated privileges in the authorization handler. - /// - public class RequiresElevationRequirement : IAuthorizationRequirement - { - } -} diff --git a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs index cdd7d8a52b..5c1029b383 100644 --- a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs +++ b/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 { /// /// Default authorization handler. /// - public class SyncPlayAccessHandler : BaseAuthorizationHandler + public class SyncPlayAccessHandler : AuthorizationHandler { private readonly ISyncPlayManager _syncPlayManager; private readonly IUserManager _userManager; @@ -23,14 +20,9 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy /// /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. - /// Instance of the interface. 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 /// 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; diff --git a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs index 6fab4c0ad8..220b223b39 100644 --- a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs +++ b/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 { /// /// The default authorization requirement. /// - public class SyncPlayAccessRequirement : IAuthorizationRequirement + public class SyncPlayAccessRequirement : DefaultAuthorizationRequirement { /// /// Initializes a new instance of the class. diff --git a/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs b/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs new file mode 100644 index 0000000000..c3de7be328 --- /dev/null +++ b/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 +{ + /// + /// Download authorization handler. + /// + public class UserPermissionHandler : AuthorizationHandler + { + private readonly IUserManager _userManager; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + public UserPermissionHandler(IUserManager userManager) + { + _userManager = userManager; + } + + /// + 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; + } + } +} diff --git a/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs b/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs new file mode 100644 index 0000000000..195a611992 --- /dev/null +++ b/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs @@ -0,0 +1,26 @@ +using Jellyfin.Api.Auth.DefaultAuthorizationPolicy; +using Jellyfin.Data.Enums; + +namespace Jellyfin.Api.Auth.DownloadPolicy +{ + /// + /// The user permission requirement. + /// + public class UserPermissionRequirement : DefaultAuthorizationRequirement + { + /// + /// Initializes a new instance of the class. + /// + /// The required . + /// Whether to validate the user's parental schedule. + public UserPermissionRequirement(PermissionKind requiredPermission, bool validateParentalSchedule = true) : base(validateParentalSchedule) + { + RequiredPermission = requiredPermission; + } + + /// + /// Gets the required user permission. + /// + public PermissionKind RequiredPermission { get; } + } +} diff --git a/Jellyfin.Api/Constants/Policies.cs b/Jellyfin.Api/Constants/Policies.cs index 5a5a2bf466..adc95e57b2 100644 --- a/Jellyfin.Api/Constants/Policies.cs +++ b/Jellyfin.Api/Constants/Policies.cs @@ -5,11 +5,6 @@ namespace Jellyfin.Api.Constants; /// public static class Policies { - /// - /// Policy name for default authorization. - /// - public const string DefaultAuthorization = "DefaultAuthorization"; - /// /// Policy name for requiring first time setup or elevated privileges. /// diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs index 069e7311b8..11933fd97f 100644 --- a/Jellyfin.Api/Controllers/ArtistsController.cs +++ b/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. /// [Route("Artists")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class ArtistsController : BaseJellyfinApiController { private readonly ILibraryManager _libraryManager; diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs index 573b7069c5..42f072f669 100644 --- a/Jellyfin.Api/Controllers/ChannelsController.cs +++ b/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; /// /// Channels Controller. /// -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class ChannelsController : BaseJellyfinApiController { private readonly IChannelManager _channelManager; diff --git a/Jellyfin.Api/Controllers/ClientLogController.cs b/Jellyfin.Api/Controllers/ClientLogController.cs index 21c31bc936..2c5dbacbbe 100644 --- a/Jellyfin.Api/Controllers/ClientLogController.cs +++ b/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; /// /// Client log controller. /// -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class ClientLogController : BaseJellyfinApiController { private const int MaxDocumentSize = 1_000_000; diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs index 5a4a9bf079..f9f9be7ce4 100644 --- a/Jellyfin.Api/Controllers/CollectionController.cs +++ b/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. /// [Route("Collections")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class CollectionController : BaseJellyfinApiController { private readonly ICollectionManager _collectionManager; diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs index d53d7cefd0..9007dfc410 100644 --- a/Jellyfin.Api/Controllers/ConfigurationController.cs +++ b/Jellyfin.Api/Controllers/ConfigurationController.cs @@ -19,7 +19,7 @@ namespace Jellyfin.Api.Controllers; /// Configuration Controller. /// [Route("System")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class ConfigurationController : BaseJellyfinApiController { private readonly IServerConfigurationManager _configurationManager; diff --git a/Jellyfin.Api/Controllers/DashboardController.cs b/Jellyfin.Api/Controllers/DashboardController.cs index f7e978bad0..076084c7a3 100644 --- a/Jellyfin.Api/Controllers/DashboardController.cs +++ b/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> GetConfigurationPages( [FromQuery] bool? enableInMainMenu) { diff --git a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs index 49d87a3621..6f0006832b 100644 --- a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs +++ b/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; /// /// Display Preferences Controller. /// -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class DisplayPreferencesController : BaseJellyfinApiController { private readonly IDisplayPreferencesManager _displayPreferencesManager; diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index b68849171f..4d8b4de24f 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/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. /// [Route("")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class DynamicHlsController : BaseJellyfinApiController { private const string DefaultVodEncoderPreset = "veryfast"; diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs index 2378aada5f..dd64ff9034 100644 --- a/Jellyfin.Api/Controllers/FilterController.cs +++ b/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. /// [Route("")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class FilterController : BaseJellyfinApiController { private readonly ILibraryManager _libraryManager; diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs index 28ebe20475..711fb4aef1 100644 --- a/Jellyfin.Api/Controllers/GenresController.cs +++ b/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; /// /// The genres controller. /// -[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); } diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index 085115e1c8..d7cec865e1 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/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 /// Hls video playlist returned. /// A containing the playlist. [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 /// Encoding stopped successfully. /// A indicating success. [HttpDelete("Videos/ActiveEncodings")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult StopEncodingProcess( [FromQuery, Required] string deviceId, diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index cc824c65ab..b2adb6a2d6 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -88,7 +88,7 @@ public class ImageController : BaseJellyfinApiController /// User does not have permission to delete the image. /// A . [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 /// User does not have permission to delete the image. /// A . [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 /// User does not have permission to delete the image. /// A . [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 /// User does not have permission to delete the image. /// A . [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 /// Item not found. /// The list of image infos on success, or if item not found. [HttpGet("Items/{itemId}/Images")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetItemImageInfos([FromRoute, Required] Guid itemId) @@ -1930,10 +1930,10 @@ public class ImageController : BaseJellyfinApiController } var responseHeaders = new Dictionary - { - { "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) { diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs index 89592bade4..43f09b49a2 100644 --- a/Jellyfin.Api/Controllers/InstantMixController.cs +++ b/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. /// [Route("")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class InstantMixController : BaseJellyfinApiController { private readonly IUserManager _userManager; diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs index c2ce4e67e5..b030e74dda 100644 --- a/Jellyfin.Api/Controllers/ItemLookupController.cs +++ b/Jellyfin.Api/Controllers/ItemLookupController.cs @@ -23,7 +23,7 @@ namespace Jellyfin.Api.Controllers; /// Item lookup controller. /// [Route("")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class ItemLookupController : BaseJellyfinApiController { private readonly IProviderManager _providerManager; diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 134974dbe0..97922d5dbc 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/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. /// [Route("")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class ItemsController : BaseJellyfinApiController { private readonly IUserManager _userManager; diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index 830f84849e..e1ad87412e 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -95,7 +95,7 @@ public class LibraryController : BaseJellyfinApiController /// Item not found. /// A with the original file. [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 /// Critic reviews returned. /// The list of critic reviews. [HttpGet("Items/{itemId}/CriticReviews")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [Obsolete("This endpoint is obsolete.")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetCriticReviews() @@ -134,7 +134,7 @@ public class LibraryController : BaseJellyfinApiController /// Item not found. /// The item theme songs. [HttpGet("Items/{itemId}/ThemeSongs")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult GetThemeSongs( @@ -200,7 +200,7 @@ public class LibraryController : BaseJellyfinApiController /// Item not found. /// The item theme videos. [HttpGet("Items/{itemId}/ThemeVideos")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult GetThemeVideos( @@ -266,7 +266,7 @@ public class LibraryController : BaseJellyfinApiController /// Item not found. /// The item theme videos. [HttpGet("Items/{itemId}/ThemeMedia")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetThemeMedia( [FromRoute, Required] Guid itemId, @@ -321,7 +321,7 @@ public class LibraryController : BaseJellyfinApiController /// Unauthorized access. /// A . [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 /// Unauthorized access. /// A . [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 /// Item counts returned. /// Item counts. [HttpGet("Items/Counts")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetItemCounts( [FromQuery] Guid? userId, @@ -426,7 +426,7 @@ public class LibraryController : BaseJellyfinApiController /// Item not found. /// Item parents. [HttpGet("Items/{itemId}/Ancestors")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult> GetAncestors([FromRoute, Required] Guid itemId, [FromQuery] Guid? userId) @@ -509,7 +509,7 @@ public class LibraryController : BaseJellyfinApiController /// A . [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 /// A . [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 /// Report success. /// A . [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> 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()) - .Distinct() - .ToArray(), + .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) + .SelectMany(i => i.SupportedImageTypes ?? Array.Empty()) + .Distinct() + .ToArray(), DefaultImageOptions = defaultImageOptions ?? Array.Empty() }); @@ -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) diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 21b4243464..c1f5d74cd9 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/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 /// [HttpGet("Info")] [ProducesResponseType(StatusCodes.Status200OK)] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] public ActionResult GetLiveTvInfo() { return _liveTvManager.GetLiveTvInfo(CancellationToken.None); @@ -131,7 +130,7 @@ public class LiveTvController : BaseJellyfinApiController /// [HttpGet("Channels")] [ProducesResponseType(StatusCodes.Status200OK)] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] public ActionResult> GetLiveTvChannels( [FromQuery] ChannelType? type, [FromQuery] Guid? userId, @@ -210,7 +209,7 @@ public class LiveTvController : BaseJellyfinApiController /// An containing the live tv channel. [HttpGet("Channels/{channelId}")] [ProducesResponseType(StatusCodes.Status200OK)] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] public ActionResult 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 /// An containing the live tv recordings. [HttpGet("Recordings")] [ProducesResponseType(StatusCodes.Status200OK)] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] public ActionResult> GetRecordings( [FromQuery] string? channelId, [FromQuery] Guid? userId, @@ -322,7 +321,7 @@ public class LiveTvController : BaseJellyfinApiController /// An containing the live tv recordings. [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 /// An containing the recording groups. [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> GetRecordingGroups([FromQuery] Guid? userId) @@ -381,7 +380,7 @@ public class LiveTvController : BaseJellyfinApiController /// An containing the recording folders. [HttpGet("Recordings/Folders")] [ProducesResponseType(StatusCodes.Status200OK)] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] public ActionResult> GetRecordingFolders([FromQuery] Guid? userId) { var user = userId is null || userId.Value.Equals(default) @@ -403,7 +402,7 @@ public class LiveTvController : BaseJellyfinApiController /// An containing the live tv recording. [HttpGet("Recordings/{recordingId}")] [ProducesResponseType(StatusCodes.Status200OK)] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] public ActionResult 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 /// A . [HttpPost("Tuners/{tunerId}/Reset")] [ProducesResponseType(StatusCodes.Status204NoContent)] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] public async Task ResetTuner([FromRoute, Required] string tunerId) { await AssertUserCanManageLiveTv().ConfigureAwait(false); @@ -443,7 +442,7 @@ public class LiveTvController : BaseJellyfinApiController /// [HttpGet("Timers/{timerId}")] [ProducesResponseType(StatusCodes.Status200OK)] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] public async Task> GetTimer([FromRoute, Required] string timerId) { return await _liveTvManager.GetTimer(timerId, CancellationToken.None).ConfigureAwait(false); @@ -459,7 +458,7 @@ public class LiveTvController : BaseJellyfinApiController /// [HttpGet("Timers/Defaults")] [ProducesResponseType(StatusCodes.Status200OK)] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] public async Task> GetDefaultTimer([FromQuery] string? programId) { return string.IsNullOrEmpty(programId) @@ -479,7 +478,7 @@ public class LiveTvController : BaseJellyfinApiController /// [HttpGet("Timers")] [ProducesResponseType(StatusCodes.Status200OK)] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] public async Task>> GetTimers( [FromQuery] string? channelId, [FromQuery] string? seriesTimerId, @@ -533,7 +532,7 @@ public class LiveTvController : BaseJellyfinApiController /// [HttpGet("Programs")] [ProducesResponseType(StatusCodes.Status200OK)] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] public async Task>> GetLiveTvPrograms( [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] channelIds, [FromQuery] Guid? userId, @@ -616,7 +615,7 @@ public class LiveTvController : BaseJellyfinApiController /// [HttpPost("Programs")] [ProducesResponseType(StatusCodes.Status200OK)] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] public async Task>> GetPrograms([FromBody] GetProgramsDto body) { var user = body.UserId.Equals(default) ? null : _userManager.GetUserById(body.UserId); @@ -682,7 +681,7 @@ public class LiveTvController : BaseJellyfinApiController /// Recommended epgs returned. /// A containing the queryresult of recommended epgs. [HttpGet("Programs/Recommended")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public async Task>> GetRecommendedPrograms( [FromQuery] Guid? userId, @@ -734,7 +733,7 @@ public class LiveTvController : BaseJellyfinApiController /// Program returned. /// An containing the livetv program. [HttpGet("Programs/{programId}")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetProgram( [FromRoute, Required] string programId, @@ -755,7 +754,7 @@ public class LiveTvController : BaseJellyfinApiController /// Item not found. /// A on success, or a if item not found. [HttpDelete("Recordings/{recordingId}")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task DeleteRecording([FromRoute, Required] Guid recordingId) @@ -783,7 +782,7 @@ public class LiveTvController : BaseJellyfinApiController /// Timer deleted. /// A . [HttpDelete("Timers/{timerId}")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task CancelTimer([FromRoute, Required] string timerId) { @@ -800,7 +799,7 @@ public class LiveTvController : BaseJellyfinApiController /// Timer updated. /// A . [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 UpdateTimer([FromRoute, Required] string timerId, [FromBody] TimerInfoDto timerInfo) @@ -817,7 +816,7 @@ public class LiveTvController : BaseJellyfinApiController /// Timer created. /// A . [HttpPost("Timers")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task CreateTimer([FromBody] TimerInfoDto timerInfo) { @@ -834,7 +833,7 @@ public class LiveTvController : BaseJellyfinApiController /// Series timer not found. /// A on success, or a if timer not found. [HttpGet("SeriesTimers/{timerId}")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetSeriesTimer([FromRoute, Required] string timerId) @@ -856,7 +855,7 @@ public class LiveTvController : BaseJellyfinApiController /// Timers returned. /// An of live tv series timers. [HttpGet("SeriesTimers")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public async Task>> GetSeriesTimers([FromQuery] string? sortBy, [FromQuery] SortOrder? sortOrder) { @@ -876,7 +875,7 @@ public class LiveTvController : BaseJellyfinApiController /// Timer cancelled. /// A . [HttpDelete("SeriesTimers/{timerId}")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task CancelSeriesTimer([FromRoute, Required] string timerId) { @@ -893,7 +892,7 @@ public class LiveTvController : BaseJellyfinApiController /// Series timer updated. /// A . [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 UpdateSeriesTimer([FromRoute, Required] string timerId, [FromBody] SeriesTimerInfoDto seriesTimerInfo) @@ -910,7 +909,7 @@ public class LiveTvController : BaseJellyfinApiController /// Series timer info created. /// A . [HttpPost("SeriesTimers")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task CreateSeriesTimer([FromBody] SeriesTimerInfoDto seriesTimerInfo) { @@ -925,7 +924,7 @@ public class LiveTvController : BaseJellyfinApiController /// Group id. /// A . [HttpGet("Recordings/Groups/{groupId}")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status404NotFound)] [Obsolete("This endpoint is obsolete.")] public ActionResult GetRecordingGroup([FromRoute, Required] Guid groupId) @@ -939,7 +938,7 @@ public class LiveTvController : BaseJellyfinApiController /// Guid info returned. /// An containing the guide info. [HttpGet("GuideInfo")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetGuideInfo() { @@ -953,7 +952,7 @@ public class LiveTvController : BaseJellyfinApiController /// Created tuner host returned. /// A containing the created tuner host. [HttpPost("TunerHosts")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> AddTunerHost([FromBody] TunerHostInfo tunerHostInfo) { @@ -967,7 +966,7 @@ public class LiveTvController : BaseJellyfinApiController /// Tuner host deleted. /// A . [HttpDelete("TunerHosts")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult DeleteTunerHost([FromQuery] string? id) { @@ -983,7 +982,7 @@ public class LiveTvController : BaseJellyfinApiController /// Default listings provider info returned. /// An containing the default listings provider info. [HttpGet("ListingProviders/Default")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetDefaultListingProvider() { @@ -1000,7 +999,7 @@ public class LiveTvController : BaseJellyfinApiController /// Created listings provider returned. /// A containing the created listings provider. [HttpPost("ListingProviders")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA5350:RemoveSha1", MessageId = "AddListingProvider", Justification = "Imported from ServiceStack")] public async Task> AddListingProvider( @@ -1026,7 +1025,7 @@ public class LiveTvController : BaseJellyfinApiController /// Listing provider deleted. /// A . [HttpDelete("ListingProviders")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult DeleteListingProvider([FromQuery] string? id) { @@ -1044,7 +1043,7 @@ public class LiveTvController : BaseJellyfinApiController /// Available lineups returned. /// A containing the available lineups. [HttpGet("ListingProviders/Lineups")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public async Task>> GetLineups( [FromQuery] string? id, @@ -1061,7 +1060,7 @@ public class LiveTvController : BaseJellyfinApiController /// Available countries returned. /// A containing the available countries. [HttpGet("ListingProviders/SchedulesDirect/Countries")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesFile(MediaTypeNames.Application.Json)] public async Task GetSchedulesDirectCountries() @@ -1082,7 +1081,7 @@ public class LiveTvController : BaseJellyfinApiController /// Channel mapping options returned. /// An containing the channel mapping options. [HttpGet("ChannelMappingOptions")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetChannelMappingOptions([FromQuery] string? providerId) { @@ -1120,7 +1119,7 @@ public class LiveTvController : BaseJellyfinApiController /// Created channel mapping returned. /// An containing the created channel mapping. [HttpPost("ChannelMappings")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> SetChannelMapping([FromBody, Required] SetChannelMappingDto setChannelMappingDto) { @@ -1133,7 +1132,7 @@ public class LiveTvController : BaseJellyfinApiController /// Tuner host types returned. /// An containing the tuner host types. [HttpGet("TunerHosts/Types")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetTunerHostTypes() { @@ -1148,7 +1147,7 @@ public class LiveTvController : BaseJellyfinApiController /// An containing the tuners. [HttpGet("Tuners/Discvover", Name = "DiscvoverTuners")] [HttpGet("Tuners/Discover")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public async Task>> DiscoverTuners([FromQuery] bool newDevicesOnly = false) { diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs index eee7df3af8..ea10dd771f 100644 --- a/Jellyfin.Api/Controllers/MediaInfoController.cs +++ b/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. /// [Route("")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class MediaInfoController : BaseJellyfinApiController { private readonly IMediaSourceManager _mediaSourceManager; diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs index 4c30dd2b37..a9336f6d24 100644 --- a/Jellyfin.Api/Controllers/MoviesController.cs +++ b/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; /// /// Movies controller. /// -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class MoviesController : BaseJellyfinApiController { private readonly IUserManager _userManager; diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs index 302f138ebc..da1a6e832c 100644 --- a/Jellyfin.Api/Controllers/MusicGenresController.cs +++ b/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; /// /// The music genres controller. /// -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class MusicGenresController : BaseJellyfinApiController { private readonly ILibraryManager _libraryManager; diff --git a/Jellyfin.Api/Controllers/PackageController.cs b/Jellyfin.Api/Controllers/PackageController.cs index 3cb3caadb3..0ba5e995fb 100644 --- a/Jellyfin.Api/Controllers/PackageController.cs +++ b/Jellyfin.Api/Controllers/PackageController.cs @@ -17,7 +17,7 @@ namespace Jellyfin.Api.Controllers; /// Package Controller. /// [Route("")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class PackageController : BaseJellyfinApiController { private readonly IInstallationManager _installationManager; diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs index 9fb6da5276..5310f50b13 100644 --- a/Jellyfin.Api/Controllers/PersonsController.cs +++ b/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; /// /// Persons controller. /// -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class PersonsController : BaseJellyfinApiController { private readonly ILibraryManager _libraryManager; diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs index 11e5893017..79c0d3c7b2 100644 --- a/Jellyfin.Api/Controllers/PlaylistsController.cs +++ b/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; /// /// Playlists controller. /// -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class PlaylistsController : BaseJellyfinApiController { private readonly IPlaylistManager _playlistManager; diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 18d6ebf1e0..11f3ddbb0a 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/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. /// [Route("")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class PlaystateController : BaseJellyfinApiController { private readonly IUserManager _userManager; diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs index 5a037d7a6b..4726cf0663 100644 --- a/Jellyfin.Api/Controllers/PluginsController.cs +++ b/Jellyfin.Api/Controllers/PluginsController.cs @@ -21,7 +21,7 @@ namespace Jellyfin.Api.Controllers; /// /// Plugins controller. /// -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class PluginsController : BaseJellyfinApiController { private readonly IInstallationManager _installationManager; diff --git a/Jellyfin.Api/Controllers/QuickConnectController.cs b/Jellyfin.Api/Controllers/QuickConnectController.cs index a58e85b2b2..503b9d3729 100644 --- a/Jellyfin.Api/Controllers/QuickConnectController.cs +++ b/Jellyfin.Api/Controllers/QuickConnectController.cs @@ -111,7 +111,7 @@ public class QuickConnectController : BaseJellyfinApiController /// Unknown user id. /// Boolean indicating if the authorization was successful. [HttpPost("Authorize")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task> AuthorizeQuickConnect([FromQuery, Required] string code, [FromQuery] Guid? userId = null) diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs index 445c5594f8..5c77db2407 100644 --- a/Jellyfin.Api/Controllers/RemoteImageController.cs +++ b/Jellyfin.Api/Controllers/RemoteImageController.cs @@ -56,7 +56,7 @@ public class RemoteImageController : BaseJellyfinApiController /// Item not found. /// Remote Image Result. [HttpGet("Items/{itemId}/RemoteImages")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetRemoteImages( @@ -121,7 +121,7 @@ public class RemoteImageController : BaseJellyfinApiController /// Item not found. /// List of remote image providers. [HttpGet("Items/{itemId}/RemoteImages/Providers")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult> GetRemoteImageProviders([FromRoute, Required] Guid itemId) diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs index 46b4920cad..a25b43345a 100644 --- a/Jellyfin.Api/Controllers/SearchController.cs +++ b/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. /// [Route("Search/Hints")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class SearchController : BaseJellyfinApiController { private readonly ISearchEngine _searchEngine; diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index ef33644785..bae8e0a490 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -56,7 +56,7 @@ public class SessionController : BaseJellyfinApiController /// List of sessions returned. /// An with the available sessions. [HttpGet("Sessions")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSessions( [FromQuery] Guid? controllableByUserId, @@ -119,7 +119,7 @@ public class SessionController : BaseJellyfinApiController /// Instruction sent to session. /// A . [HttpPost("Sessions/{sessionId}/Viewing")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task DisplayContent( [FromRoute, Required] string sessionId, @@ -158,7 +158,7 @@ public class SessionController : BaseJellyfinApiController /// Instruction sent to session. /// A . [HttpPost("Sessions/{sessionId}/Playing")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task Play( [FromRoute, Required] string sessionId, @@ -201,7 +201,7 @@ public class SessionController : BaseJellyfinApiController /// Playstate command sent to session. /// A . [HttpPost("Sessions/{sessionId}/Playing/{command}")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task SendPlaystateCommand( [FromRoute, Required] string sessionId, @@ -232,7 +232,7 @@ public class SessionController : BaseJellyfinApiController /// System command sent to session. /// A . [HttpPost("Sessions/{sessionId}/System/{command}")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task SendSystemCommand( [FromRoute, Required] string sessionId, @@ -258,7 +258,7 @@ public class SessionController : BaseJellyfinApiController /// General command sent to session. /// A . [HttpPost("Sessions/{sessionId}/Command/{command}")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task SendGeneralCommand( [FromRoute, Required] string sessionId, @@ -286,7 +286,7 @@ public class SessionController : BaseJellyfinApiController /// Full general command sent to session. /// A . [HttpPost("Sessions/{sessionId}/Command")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task SendFullGeneralCommand( [FromRoute, Required] string sessionId, @@ -316,7 +316,7 @@ public class SessionController : BaseJellyfinApiController /// Message sent. /// A . [HttpPost("Sessions/{sessionId}/Message")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task SendMessageCommand( [FromRoute, Required] string sessionId, @@ -345,7 +345,7 @@ public class SessionController : BaseJellyfinApiController /// User added to session. /// A . [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 /// User removed from session. /// A . [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 /// Capabilities posted. /// A . [HttpPost("Sessions/Capabilities")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task PostCapabilities( [FromQuery] string? id, @@ -419,7 +419,7 @@ public class SessionController : BaseJellyfinApiController /// Capabilities updated. /// A . [HttpPost("Sessions/Capabilities/Full")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task PostFullCapabilities( [FromQuery] string? id, @@ -443,7 +443,7 @@ public class SessionController : BaseJellyfinApiController /// Session reported to server. /// A . [HttpPost("Sessions/Viewing")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task ReportViewing( [FromQuery] string? sessionId, @@ -461,7 +461,7 @@ public class SessionController : BaseJellyfinApiController /// Session end reported to server. /// A . [HttpPost("Sessions/Logout")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task ReportSessionEnded() { diff --git a/Jellyfin.Api/Controllers/StudiosController.cs b/Jellyfin.Api/Controllers/StudiosController.cs index 799be2ae86..21965e956d 100644 --- a/Jellyfin.Api/Controllers/StudiosController.cs +++ b/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; /// /// Studios controller. /// -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class StudiosController : BaseJellyfinApiController { private readonly ILibraryManager _libraryManager; diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs index fd0a71f9e3..e384213380 100644 --- a/Jellyfin.Api/Controllers/SubtitleController.cs +++ b/Jellyfin.Api/Controllers/SubtitleController.cs @@ -114,7 +114,7 @@ public class SubtitleController : BaseJellyfinApiController /// Subtitles retrieved. /// An array of . [HttpGet("Items/{itemId}/RemoteSearch/Subtitles/{language}")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public async Task>> SearchRemoteSubtitles( [FromRoute, Required] Guid itemId, @@ -134,7 +134,7 @@ public class SubtitleController : BaseJellyfinApiController /// Subtitle downloaded. /// A . [HttpPost("Items/{itemId}/RemoteSearch/Subtitles/{subtitleId}")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task DownloadRemoteSubtitles( [FromRoute, Required] Guid itemId, @@ -164,7 +164,7 @@ public class SubtitleController : BaseJellyfinApiController /// File returned. /// A with the subtitle file. [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 /// Subtitle playlist retrieved. /// A with the HLS subtitle playlist. [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 /// Information retrieved. /// An array of with the available font files. [HttpGet("FallbackFont/Fonts")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public IEnumerable GetFallbackFontList() { @@ -514,7 +514,7 @@ public class SubtitleController : BaseJellyfinApiController /// Fallback font file retrieved. /// The fallback font file. [HttpGet("FallbackFont/Fonts/{name}")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesFile("font/*")] public ActionResult GetFallbackFont([FromRoute, Required] string name) diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs index c5c429757a..5b808f257c 100644 --- a/Jellyfin.Api/Controllers/SuggestionsController.cs +++ b/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. /// [Route("")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class SuggestionsController : BaseJellyfinApiController { private readonly IDtoService _dtoService; diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs index b0b2e2d6d8..4ab705f40a 100644 --- a/Jellyfin.Api/Controllers/SystemController.cs +++ b/Jellyfin.Api/Controllers/SystemController.cs @@ -172,7 +172,7 @@ public class SystemController : BaseJellyfinApiController /// Information retrieved. /// with information about the endpoint. [HttpGet("Endpoint")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetEndpointInfo() { @@ -210,7 +210,7 @@ public class SystemController : BaseJellyfinApiController /// Information retrieved. /// An with the WakeOnLan infos. [HttpGet("WakeOnLanInfo")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [Obsolete("This endpoint is obsolete.")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetWakeOnLanInfo() diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs index 115efcd8f3..b5b6406206 100644 --- a/Jellyfin.Api/Controllers/TrailersController.cs +++ b/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; /// /// The trailers controller. /// -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class TrailersController : BaseJellyfinApiController { private readonly ItemsController _itemsController; diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs index 2be32095e3..b0760f97c7 100644 --- a/Jellyfin.Api/Controllers/TvShowsController.cs +++ b/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. /// [Route("Shows")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class TvShowsController : BaseJellyfinApiController { private readonly IUserManager _userManager; diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 6946caa2ba..3455215979 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/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 /// A containing the audio file. [HttpGet("Audio/{itemId}/universal")] [HttpHead("Audio/{itemId}/universal", Name = "HeadUniversalAudioStream")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status302Found)] [ProducesAudioFile] diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 7f184f31e7..d9ea96f2d7 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -81,7 +81,7 @@ public class UserController : BaseJellyfinApiController /// Users returned. /// An containing the users. [HttpGet] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetUsers( [FromQuery] bool? isHidden, @@ -251,7 +251,7 @@ public class UserController : BaseJellyfinApiController /// User not found. /// A indicating success or a or a on failure. [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 /// User not found. /// A indicating success or a or a on failure. [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 /// User update forbidden. /// A indicating success or a or a on failure. [HttpPost("{userId}")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] @@ -440,7 +440,7 @@ public class UserController : BaseJellyfinApiController /// User configuration update forbidden. /// A indicating success. [HttpPost("{userId}/Configuration")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task UpdateUserConfiguration( @@ -526,7 +526,7 @@ public class UserController : BaseJellyfinApiController /// Token is not owned by a user. /// A for the authenticated user. [HttpGet("Me")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public ActionResult GetCurrentUser() diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 556cf38945..93312b817f 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/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. /// [Route("")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class UserLibraryController : BaseJellyfinApiController { private readonly IUserManager _userManager; diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs index aa7ba8891b..838b432340 100644 --- a/Jellyfin.Api/Controllers/UserViewsController.cs +++ b/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. /// [Route("")] -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class UserViewsController : BaseJellyfinApiController { private readonly IUserManager _userManager; diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 01a319879a..9299991966 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -100,7 +100,7 @@ public class VideosController : BaseJellyfinApiController /// Additional parts returned. /// A with the parts. [HttpGet("{itemId}/AdditionalParts")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetAdditionalPart([FromRoute, Required] Guid itemId, [FromQuery] Guid? userId) { diff --git a/Jellyfin.Api/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs index 2e5fdc1464..def37cb971 100644 --- a/Jellyfin.Api/Controllers/YearsController.cs +++ b/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; /// /// Years controller. /// -[Authorize(Policy = Policies.DefaultAuthorization)] +[Authorize] public class YearsController : BaseJellyfinApiController { private readonly ILibraryManager _libraryManager; diff --git a/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs b/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs index 7b05351e32..9c2194fafd 100644 --- a/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs +++ b/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 /// The async task. 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; } diff --git a/Jellyfin.Data/DayOfWeekHelper.cs b/Jellyfin.Data/DayOfWeekHelper.cs index b7ba30180e..d1ce8185f2 100644 --- a/Jellyfin.Data/DayOfWeekHelper.cs +++ b/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 + }; + } } } diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index eb59e70f3b..4ce581749f 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/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; } diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index e9af1cf83c..e2dcaf5f5c 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/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 /// The updated service collection. public static IServiceCollection AddJellyfinApiAuthorization(this IServiceCollection serviceCollection) { + // The default handler must be first so that it is evaluated first serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + 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); + }); + } + /// /// Sets up the proxy configuration based on the addresses in . /// diff --git a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs similarity index 80% rename from tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs rename to tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs index ee42216e46..6669a6689c 100644 --- a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs +++ b/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 _configurationManagerMock; private readonly List _requirements; - private readonly FirstTimeSetupOrElevatedHandler _sut; + private readonly FirstTimeSetupHandler _firstTimeSetupHandler; private readonly Mock _userManagerMock; private readonly Mock _httpContextAccessor; - public FirstTimeSetupOrElevatedHandlerTests() + public FirstTimeSetupHandlerTests() { var fixture = new Fixture().Customize(new AutoMoqCustomization()); _configurationManagerMock = fixture.Freeze>(); - _requirements = new List { new FirstTimeSetupOrElevatedRequirement() }; + _requirements = new List { new FirstTimeSetupRequirement() }; _userManagerMock = fixture.Freeze>(); _httpContextAccessor = fixture.Freeze>(); - _sut = fixture.Create(); + _firstTimeSetupHandler = fixture.Create(); } [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); } } diff --git a/tests/Jellyfin.Api.Tests/Auth/IgnoreSchedulePolicy/IgnoreScheduleHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/IgnoreSchedulePolicy/IgnoreScheduleHandlerTests.cs index 7150c90bb8..9cf8f85483 100644 --- a/tests/Jellyfin.Api.Tests/Auth/IgnoreSchedulePolicy/IgnoreScheduleHandlerTests.cs +++ b/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 _configurationManagerMock; private readonly List _requirements; - private readonly IgnoreParentalControlHandler _sut; + private readonly DefaultAuthorizationHandler _sut; private readonly Mock _userManagerMock; private readonly Mock _httpContextAccessor; @@ -33,11 +33,11 @@ namespace Jellyfin.Api.Tests.Auth.IgnoreSchedulePolicy { var fixture = new Fixture().Customize(new AutoMoqCustomization()); _configurationManagerMock = fixture.Freeze>(); - _requirements = new List { new IgnoreParentalControlRequirement() }; + _requirements = new List { new DefaultAuthorizationRequirement(validateParentalSchedule: false) }; _userManagerMock = fixture.Freeze>(); _httpContextAccessor = fixture.Freeze>(); - _sut = fixture.Create(); + _sut = fixture.Create(); } [Theory] diff --git a/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs deleted file mode 100644 index 5b3d784ffa..0000000000 --- a/tests/Jellyfin.Api.Tests/Auth/LocalAccessPolicy/LocalAccessHandlerTests.cs +++ /dev/null @@ -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 _configurationManagerMock; - private readonly List _requirements; - private readonly LocalAccessHandler _sut; - private readonly Mock _userManagerMock; - private readonly Mock _httpContextAccessor; - private readonly Mock _networkManagerMock; - - public LocalAccessHandlerTests() - { - var fixture = new Fixture().Customize(new AutoMoqCustomization()); - _configurationManagerMock = fixture.Freeze>(); - _requirements = new List { new LocalAccessRequirement() }; - _userManagerMock = fixture.Freeze>(); - _httpContextAccessor = fixture.Freeze>(); - _networkManagerMock = fixture.Freeze>(); - - _sut = fixture.Create(); - } - - [Theory] - [InlineData(true, true)] - [InlineData(false, false)] - public async Task LocalAccessOnly(bool isInLocalNetwork, bool shouldSucceed) - { - _networkManagerMock - .Setup(n => n.IsInLocalNetwork(It.IsAny())) - .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); - } - } -} diff --git a/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs deleted file mode 100644 index ffe88fcdeb..0000000000 --- a/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs +++ /dev/null @@ -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 _configurationManagerMock; - private readonly List _requirements; - private readonly RequiresElevationHandler _sut; - private readonly Mock _userManagerMock; - private readonly Mock _httpContextAccessor; - - public RequiresElevationHandlerTests() - { - var fixture = new Fixture().Customize(new AutoMoqCustomization()); - _configurationManagerMock = fixture.Freeze>(); - _requirements = new List { new RequiresElevationRequirement() }; - _userManagerMock = fixture.Freeze>(); - _httpContextAccessor = fixture.Freeze>(); - - _sut = fixture.Create(); - } - - [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); - } - } -}