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";
}