diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs
index 1233995b4c..2c4fe91862 100644
--- a/Jellyfin.Api/Controllers/UserLibraryController.cs
+++ b/Jellyfin.Api/Controllers/UserLibraryController.cs
@@ -6,6 +6,7 @@ using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.ModelBinders;
+using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -72,7 +73,7 @@ public class UserLibraryController : BaseJellyfinApiController
/// User id.
/// Item id.
/// Item returned.
- /// An containing the d item.
+ /// An containing the item.
[HttpGet("Users/{userId}/Items/{itemId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task> GetItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
@@ -86,11 +87,19 @@ public class UserLibraryController : BaseJellyfinApiController
var item = itemId.Equals(default)
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
+
if (item is null)
{
return NotFound();
}
+ if (item is not UserRootFolder
+ // Check the item is visible for the user
+ && !item.IsVisible(user))
+ {
+ return Unauthorized($"{user.Username} is not permitted to access item {item.Name}.");
+ }
+
await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false);
var dtoOptions = new DtoOptions().AddClientFields(User);
@@ -139,11 +148,19 @@ public class UserLibraryController : BaseJellyfinApiController
var item = itemId.Equals(default)
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
+
if (item is null)
{
return NotFound();
}
+ if (item is not UserRootFolder
+ // Check the item is visible for the user
+ && !item.IsVisible(user))
+ {
+ return Unauthorized($"{user.Username} is not permitted to access item {item.Name}.");
+ }
+
var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false);
var dtoOptions = new DtoOptions().AddClientFields(User);
var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray();
@@ -162,7 +179,29 @@ public class UserLibraryController : BaseJellyfinApiController
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult MarkFavoriteItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
{
- return MarkFavorite(userId, itemId, true);
+ var user = _userManager.GetUserById(userId);
+ if (user is null)
+ {
+ return NotFound();
+ }
+
+ var item = itemId.Equals(default)
+ ? _libraryManager.GetUserRootFolder()
+ : _libraryManager.GetItemById(itemId);
+
+ if (item is null)
+ {
+ return NotFound();
+ }
+
+ if (item is not UserRootFolder
+ // Check the item is visible for the user
+ && !item.IsVisible(user))
+ {
+ return Unauthorized($"{user.Username} is not permitted to access item {item.Name}.");
+ }
+
+ return MarkFavorite(user, item, true);
}
///
@@ -176,7 +215,29 @@ public class UserLibraryController : BaseJellyfinApiController
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult UnmarkFavoriteItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
{
- return MarkFavorite(userId, itemId, false);
+ var user = _userManager.GetUserById(userId);
+ if (user is null)
+ {
+ return NotFound();
+ }
+
+ var item = itemId.Equals(default)
+ ? _libraryManager.GetUserRootFolder()
+ : _libraryManager.GetItemById(itemId);
+
+ if (item is null)
+ {
+ return NotFound();
+ }
+
+ if (item is not UserRootFolder
+ // Check the item is visible for the user
+ && !item.IsVisible(user))
+ {
+ return Unauthorized($"{user.Username} is not permitted to access item {item.Name}.");
+ }
+
+ return MarkFavorite(user, item, false);
}
///
@@ -190,7 +251,29 @@ public class UserLibraryController : BaseJellyfinApiController
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult DeleteUserItemRating([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
{
- return UpdateUserItemRatingInternal(userId, itemId, null);
+ var user = _userManager.GetUserById(userId);
+ if (user is null)
+ {
+ return NotFound();
+ }
+
+ var item = itemId.Equals(default)
+ ? _libraryManager.GetUserRootFolder()
+ : _libraryManager.GetItemById(itemId);
+
+ if (item is null)
+ {
+ return NotFound();
+ }
+
+ if (item is not UserRootFolder
+ // Check the item is visible for the user
+ && !item.IsVisible(user))
+ {
+ return Unauthorized($"{user.Username} is not permitted to access item {item.Name}.");
+ }
+
+ return UpdateUserItemRatingInternal(user, item, null);
}
///
@@ -205,7 +288,29 @@ public class UserLibraryController : BaseJellyfinApiController
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult UpdateUserItemRating([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId, [FromQuery] bool? likes)
{
- return UpdateUserItemRatingInternal(userId, itemId, likes);
+ var user = _userManager.GetUserById(userId);
+ if (user is null)
+ {
+ return NotFound();
+ }
+
+ var item = itemId.Equals(default)
+ ? _libraryManager.GetUserRootFolder()
+ : _libraryManager.GetItemById(itemId);
+
+ if (item is null)
+ {
+ return NotFound();
+ }
+
+ if (item is not UserRootFolder
+ // Check the item is visible for the user
+ && !item.IsVisible(user))
+ {
+ return Unauthorized($"{user.Username} is not permitted to access item {item.Name}.");
+ }
+
+ return UpdateUserItemRatingInternal(user, item, likes);
}
///
@@ -228,13 +333,20 @@ public class UserLibraryController : BaseJellyfinApiController
var item = itemId.Equals(default)
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
+
if (item is null)
{
return NotFound();
}
- var dtoOptions = new DtoOptions().AddClientFields(User);
+ if (item is not UserRootFolder
+ // Check the item is visible for the user
+ && !item.IsVisible(user))
+ {
+ return Unauthorized($"{user.Username} is not permitted to access item {item.Name}.");
+ }
+ var dtoOptions = new DtoOptions().AddClientFields(User);
if (item is IHasTrailers hasTrailers)
{
var trailers = hasTrailers.LocalTrailers;
@@ -266,11 +378,19 @@ public class UserLibraryController : BaseJellyfinApiController
var item = itemId.Equals(default)
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
+
if (item is null)
{
return NotFound();
}
+ if (item is not UserRootFolder
+ // Check the item is visible for the user
+ && !item.IsVisible(user))
+ {
+ return Unauthorized($"{user.Username} is not permitted to access item {item.Name}.");
+ }
+
var dtoOptions = new DtoOptions().AddClientFields(User);
return Ok(item
@@ -385,15 +505,11 @@ public class UserLibraryController : BaseJellyfinApiController
///
/// Marks the favorite.
///
- /// The user id.
- /// The item id.
+ /// The user.
+ /// The item.
/// if set to true [is favorite].
- private UserItemDataDto MarkFavorite(Guid userId, Guid itemId, bool isFavorite)
+ private UserItemDataDto MarkFavorite(User user, BaseItem item, bool isFavorite)
{
- var user = _userManager.GetUserById(userId);
-
- var item = itemId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId);
-
// Get the user data for this item
var data = _userDataRepository.GetUserData(user, item);
@@ -408,15 +524,11 @@ public class UserLibraryController : BaseJellyfinApiController
///
/// Updates the user item rating.
///
- /// The user id.
- /// The item id.
+ /// The user.
+ /// The item.
/// if set to true [likes].
- private UserItemDataDto UpdateUserItemRatingInternal(Guid userId, Guid itemId, bool? likes)
+ private UserItemDataDto UpdateUserItemRatingInternal(User user, BaseItem item, bool? likes)
{
- var user = _userManager.GetUserById(userId);
-
- var item = itemId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId);
-
// Get the user data for this item
var data = _userDataRepository.GetUserData(user, item);
@@ -455,6 +567,13 @@ public class UserLibraryController : BaseJellyfinApiController
return NotFound();
}
+ if (item is not UserRootFolder
+ // Check the item is visible for the user
+ && !item.IsVisible(user))
+ {
+ return Unauthorized($"{user.Username} is not permitted to access item {item.Name}.");
+ }
+
var result = await _lyricManager.GetLyrics(item).ConfigureAwait(false);
if (result is not null)
{