diff --git a/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs b/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs index 557b7d3aa4..f4be266c18 100644 --- a/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs +++ b/Jellyfin.Api/Auth/LocalAccessOrRequiresElevationPolicy/LocalAccessOrRequiresElevationHandler.cs @@ -34,20 +34,9 @@ namespace Jellyfin.Api.Auth.LocalAccessOrRequiresElevationPolicy var ip = _httpContextAccessor.HttpContext?.GetNormalizedRemoteIP(); // Loopback will be on LAN, so we can accept null. - if (ip is null || _networkManager.IsInLocalNetwork(ip)) + if (ip is null || _networkManager.IsInLocalNetwork(ip) || context.User.IsInRole(UserRoles.Administrator)) { context.Succeed(requirement); - - return Task.CompletedTask; - } - - if (context.User.IsInRole(UserRoles.Administrator)) - { - context.Succeed(requirement); - } - else - { - context.Fail(); } return Task.CompletedTask; diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 72be555133..64592afecd 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -4,10 +4,11 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Helpers; -using Jellyfin.Api.Models.StreamingDtos; +using MediaBrowser.Common.Api; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Streaming; using MediaBrowser.Model.Dlna; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -16,6 +17,7 @@ namespace Jellyfin.Api.Controllers; /// /// The audio controller. /// +[Authorize(Policy = Policies.Playback)] public class AudioController : BaseJellyfinApiController { private readonly AudioHelper _audioHelper; diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 68602c80d5..e843287088 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -15,6 +15,7 @@ using Jellyfin.Api.Models.StreamingDtos; using Jellyfin.Data.Enums; using Jellyfin.Extensions; using Jellyfin.MediaEncoding.Hls.Playlist; +using MediaBrowser.Common.Api; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; @@ -36,16 +37,14 @@ namespace Jellyfin.Api.Controllers; /// /// Dynamic hls controller. /// -[Route("")] -[Authorize] +[Authorize(Policy = Policies.Playback)] public class DynamicHlsController : BaseJellyfinApiController { private const string DefaultVodEncoderPreset = "veryfast"; private const string DefaultEventEncoderPreset = "superfast"; - private const TranscodingJobType TranscodingJobType = MediaBrowser.Controller.MediaEncoding.TranscodingJobType.Hls; + private const TranscodingJobType DefaultTranscodingJobType = TranscodingJobType.Hls; private readonly Version _minFFmpegFlacInMp4 = new Version(6, 0); - private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; private readonly IMediaSourceManager _mediaSourceManager; @@ -285,7 +284,7 @@ public class DynamicHlsController : BaseJellyfinApiController _mediaEncoder, _encodingHelper, _transcodeManager, - TranscodingJobType, + DefaultTranscodingJobType, cancellationToken) .ConfigureAwait(false); @@ -306,7 +305,7 @@ public class DynamicHlsController : BaseJellyfinApiController playlistPath, GetCommandLineArguments(playlistPath, state, true, 0), Request.HttpContext.User.GetUserId(), - TranscodingJobType, + DefaultTranscodingJobType, cancellationTokenSource) .ConfigureAwait(false); job.IsLiveOutput = true; @@ -326,7 +325,7 @@ public class DynamicHlsController : BaseJellyfinApiController } } - job ??= _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); + job ??= _transcodeManager.OnTranscodeBeginRequest(playlistPath, DefaultTranscodingJobType); if (job is not null) { @@ -508,7 +507,7 @@ public class DynamicHlsController : BaseJellyfinApiController EnableTrickplay = enableTrickplay }; - return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); + return await _dynamicHlsHelper.GetMasterHlsPlaylist(DefaultTranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); } /// @@ -674,7 +673,7 @@ public class DynamicHlsController : BaseJellyfinApiController EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming }; - return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); + return await _dynamicHlsHelper.GetMasterHlsPlaylist(DefaultTranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); } /// @@ -1379,7 +1378,7 @@ public class DynamicHlsController : BaseJellyfinApiController _mediaEncoder, _encodingHelper, _transcodeManager, - TranscodingJobType, + DefaultTranscodingJobType, cancellationTokenSource.Token) .ConfigureAwait(false); @@ -1417,7 +1416,7 @@ public class DynamicHlsController : BaseJellyfinApiController _mediaEncoder, _encodingHelper, _transcodeManager, - TranscodingJobType, + DefaultTranscodingJobType, cancellationToken) .ConfigureAwait(false); @@ -1431,7 +1430,7 @@ public class DynamicHlsController : BaseJellyfinApiController if (System.IO.File.Exists(segmentPath)) { - job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); + job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, DefaultTranscodingJobType); _logger.LogDebug("returning {0} [it exists, try 1]", segmentPath); return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false); } @@ -1441,7 +1440,7 @@ public class DynamicHlsController : BaseJellyfinApiController var startTranscoding = false; if (System.IO.File.Exists(segmentPath)) { - job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); + job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, DefaultTranscodingJobType); _logger.LogDebug("returning {0} [it exists, try 2]", segmentPath); return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false); } @@ -1492,7 +1491,7 @@ public class DynamicHlsController : BaseJellyfinApiController playlistPath, GetCommandLineArguments(playlistPath, state, false, segmentId), Request.HttpContext.User.GetUserId(), - TranscodingJobType, + DefaultTranscodingJobType, cancellationTokenSource).ConfigureAwait(false); } catch @@ -1505,7 +1504,7 @@ public class DynamicHlsController : BaseJellyfinApiController } else { - job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); + job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, DefaultTranscodingJobType); if (job?.TranscodingThrottler is not null) { await job.TranscodingThrottler.UnpauseTranscoding().ConfigureAwait(false); @@ -1514,7 +1513,7 @@ public class DynamicHlsController : BaseJellyfinApiController } _logger.LogDebug("returning {0} [general case]", segmentPath); - job ??= _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); + job ??= _transcodeManager.OnTranscodeBeginRequest(playlistPath, DefaultTranscodingJobType); return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false); } @@ -1970,7 +1969,7 @@ public class DynamicHlsController : BaseJellyfinApiController private int? GetCurrentTranscodingIndex(string playlist, string segmentExtension) { - var job = _transcodeManager.GetTranscodingJob(playlist, TranscodingJobType); + var job = _transcodeManager.GetTranscodingJob(playlist, DefaultTranscodingJobType); if (job is null || job.HasExited) { diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index 1927a332b2..d9a725b75e 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs @@ -5,6 +5,7 @@ using System.IO; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Helpers; +using MediaBrowser.Common.Api; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.MediaEncoding; @@ -20,6 +21,7 @@ namespace Jellyfin.Api.Controllers; /// The hls segment controller. /// [Route("")] +[Authorize(Policy = Policies.Playback)] public class HlsSegmentController : BaseJellyfinApiController { private readonly IFileSystem _fileSystem; diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 2b26c01f88..93ebb0ec51 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -1156,6 +1156,7 @@ public class LiveTvController : BaseJellyfinApiController /// or a if recording not found. /// [HttpGet("LiveRecordings/{recordingId}/stream")] + [Authorize(Policy = Policies.Playback)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesVideoFile] @@ -1183,6 +1184,7 @@ public class LiveTvController : BaseJellyfinApiController /// or a if stream not found. /// [HttpGet("LiveStreamFiles/{streamId}/stream.{container}")] + [Authorize(Policy = Policies.Playback)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesVideoFile] diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 1d4adae067..53add1828f 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -8,6 +8,7 @@ using Jellyfin.Api.Attributes; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.StreamingDtos; +using MediaBrowser.Common.Api; using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; @@ -28,6 +29,7 @@ namespace Jellyfin.Api.Controllers; /// The universal audio controller. /// [Route("")] +[Authorize(Policy = Policies.Playback)] public class UniversalAudioController : BaseJellyfinApiController { private readonly ILibraryManager _libraryManager; @@ -89,7 +91,6 @@ public class UniversalAudioController : BaseJellyfinApiController /// A containing the audio file. [HttpGet("Audio/{itemId}/universal")] [HttpHead("Audio/{itemId}/universal", Name = "HeadUniversalAudioStream")] - [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status302Found)] [ProducesResponseType(StatusCodes.Status404NotFound)] diff --git a/Jellyfin.Api/Controllers/VideoAttachmentsController.cs b/Jellyfin.Api/Controllers/VideoAttachmentsController.cs index b67c6fdb7b..2a394d5113 100644 --- a/Jellyfin.Api/Controllers/VideoAttachmentsController.cs +++ b/Jellyfin.Api/Controllers/VideoAttachmentsController.cs @@ -10,6 +10,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -19,6 +20,7 @@ namespace Jellyfin.Api.Controllers; /// Attachments controller. /// [Route("Videos")] +[Authorize] public class VideoAttachmentsController : BaseJellyfinApiController { private readonly ILibraryManager _libraryManager; diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index a9e1d44846..d2b44d50ba 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -310,6 +310,7 @@ public class VideosController : BaseJellyfinApiController /// A containing the audio file. [HttpGet("{itemId}/stream")] [HttpHead("{itemId}/stream", Name = "HeadVideoStream")] + [Authorize(Policy = Policies.Playback)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesVideoFile] public async Task GetVideoStream( @@ -548,6 +549,7 @@ public class VideosController : BaseJellyfinApiController /// A containing the audio file. [HttpGet("{itemId}/stream.{container}")] [HttpHead("{itemId}/stream.{container}", Name = "HeadVideoStreamByContainer")] + [Authorize(Policy = Policies.Playback)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesVideoFile] public Task GetVideoStreamByContainer( diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 597643ed19..0945d9ae28 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -77,6 +77,7 @@ namespace Jellyfin.Server.Extensions options.AddPolicy(Policies.LiveTvAccess, new UserPermissionRequirement(PermissionKind.EnableLiveTvAccess)); options.AddPolicy(Policies.LiveTvManagement, new UserPermissionRequirement(PermissionKind.EnableLiveTvManagement)); options.AddPolicy(Policies.LocalAccessOrRequiresElevation, new LocalAccessOrRequiresElevationRequirement()); + options.AddPolicy(Policies.Playback, new UserPermissionRequirement(PermissionKind.EnableMediaPlayback)); options.AddPolicy(Policies.SyncPlayHasAccess, new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.HasAccess)); options.AddPolicy(Policies.SyncPlayCreateGroup, new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.CreateGroup)); options.AddPolicy(Policies.SyncPlayJoinGroup, new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.JoinGroup)); diff --git a/MediaBrowser.Common/Api/Policies.cs b/MediaBrowser.Common/Api/Policies.cs index 435f4798ff..1d32a3c9c8 100644 --- a/MediaBrowser.Common/Api/Policies.cs +++ b/MediaBrowser.Common/Api/Policies.cs @@ -94,4 +94,9 @@ public static class Policies /// Policy name for accessing lyric management. /// public const string LyricManagement = "LyricManagement"; + + /// + /// Policy name for playback. + /// + public const string Playback = "Playback"; }