diff --git a/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs b/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs
index ba2b1b657e..e72bec46fd 100644
--- a/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs
+++ b/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs
@@ -1,5 +1,4 @@
using System.Threading.Tasks;
-using Jellyfin.Api.Auth.DownloadPolicy;
using Jellyfin.Api.Extensions;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Library;
@@ -8,7 +7,7 @@ using Microsoft.AspNetCore.Authorization;
namespace Jellyfin.Api.Auth.UserPermissionPolicy
{
///
- /// Download authorization handler.
+ /// User permission authorization handler.
///
public class UserPermissionHandler : AuthorizationHandler
{
diff --git a/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs b/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs
index 195a611992..4694556eb7 100644
--- a/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs
+++ b/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionRequirement.cs
@@ -1,7 +1,7 @@
using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
using Jellyfin.Data.Enums;
-namespace Jellyfin.Api.Auth.DownloadPolicy
+namespace Jellyfin.Api.Auth.UserPermissionPolicy
{
///
/// The user permission requirement.
diff --git a/Jellyfin.Api/Constants/Policies.cs b/Jellyfin.Api/Constants/Policies.cs
index adc95e57b2..1ef38ca072 100644
--- a/Jellyfin.Api/Constants/Policies.cs
+++ b/Jellyfin.Api/Constants/Policies.cs
@@ -69,4 +69,9 @@ public static class Policies
/// Policy name for accessing a SyncPlay group.
///
public const string SyncPlayIsInGroup = "SyncPlayIsInGroup";
+
+ ///
+ /// Policy name for accessing collection management.
+ ///
+ public const string CollectionManagement = "CollectionManagement";
}
diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs
index f9f9be7ce4..2db04afb80 100644
--- a/Jellyfin.Api/Controllers/CollectionController.cs
+++ b/Jellyfin.Api/Controllers/CollectionController.cs
@@ -1,6 +1,7 @@
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;
@@ -16,7 +17,7 @@ namespace Jellyfin.Api.Controllers;
/// The collection controller.
///
[Route("Collections")]
-[Authorize]
+[Authorize(Policy = Policies.CollectionManagement)]
public class CollectionController : BaseJellyfinApiController
{
private readonly ICollectionManager _collectionManager;
diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs
index 4ce581749f..606e1b5427 100644
--- a/Jellyfin.Data/Entities/User.cs
+++ b/Jellyfin.Data/Entities/User.cs
@@ -508,6 +508,7 @@ namespace Jellyfin.Data.Entities
Permissions.Add(new Permission(PermissionKind.EnableVideoPlaybackTranscoding, true));
Permissions.Add(new Permission(PermissionKind.ForceRemoteSourceTranscoding, false));
Permissions.Add(new Permission(PermissionKind.EnableRemoteControlOfOtherUsers, false));
+ Permissions.Add(new Permission(PermissionKind.EnableCollectionManagement, false));
}
///
diff --git a/Jellyfin.Data/Enums/PermissionKind.cs b/Jellyfin.Data/Enums/PermissionKind.cs
index 7d52008747..40280b95ef 100644
--- a/Jellyfin.Data/Enums/PermissionKind.cs
+++ b/Jellyfin.Data/Enums/PermissionKind.cs
@@ -108,6 +108,11 @@ namespace Jellyfin.Data.Enums
///
/// Whether the server should force transcoding on remote connections for the user.
///
- ForceRemoteSourceTranscoding = 20
+ ForceRemoteSourceTranscoding = 20,
+
+ ///
+ /// Whether the user can create, modify and delete collections.
+ ///
+ EnableCollectionManagement = 21
}
}
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index f9679510d7..92384986af 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -369,6 +369,7 @@ namespace Jellyfin.Server.Implementations.Users
EnablePlaybackRemuxing = user.HasPermission(PermissionKind.EnablePlaybackRemuxing),
ForceRemoteSourceTranscoding = user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding),
EnablePublicSharing = user.HasPermission(PermissionKind.EnablePublicSharing),
+ EnableCollectionManagement = user.HasPermission(PermissionKind.EnableCollectionManagement),
AccessSchedules = user.AccessSchedules.ToArray(),
BlockedTags = user.GetPreference(PreferenceKind.BlockedTags),
AllowedTags = user.GetPreference(PreferenceKind.AllowedTags),
@@ -685,6 +686,7 @@ namespace Jellyfin.Server.Implementations.Users
user.SetPermission(PermissionKind.EnableAllFolders, policy.EnableAllFolders);
user.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, policy.EnableRemoteControlOfOtherUsers);
user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing);
+ user.SetPermission(PermissionKind.EnableCollectionManagement, policy.EnableCollectionManagement);
user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding);
user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing);
diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index 968a8e58ce..dffcfbba87 100644
--- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
@@ -10,7 +10,6 @@ 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.FirstTimeSetupPolicy;
using Jellyfin.Api.Auth.SyncPlayAccessPolicy;
using Jellyfin.Api.Auth.UserPermissionPolicy;
@@ -75,6 +74,7 @@ namespace Jellyfin.Server.Extensions
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.CollectionManagement, new UserPermissionRequirement(PermissionKind.EnableCollectionManagement));
options.AddPolicy(Policies.AnonymousLanAccessPolicy, new AnonymousLanAccessRequirement());
options.AddPolicy(
Policies.RequiresElevation,
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
index ea2f033027..9bf1e6b808 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
@@ -163,6 +163,7 @@ namespace Jellyfin.Server.Migrations.Routines
user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing);
user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding);
user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing);
+ user.SetPermission(PermissionKind.EnableCollectionManagement, policy.EnableCollectionManagement);
foreach (var policyAccessSchedule in policy.AccessSchedules)
{
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index 882abc9272..66210cb6c4 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -104,7 +104,7 @@ namespace MediaBrowser.Controller.Entities.Movies
public override bool IsAuthorizedToDelete(User user, List allCollectionFolders)
{
- return true;
+ return user.HasPermission(PermissionKind.IsAdministrator) || user.HasPermission(PermissionKind.EnableCollectionManagement);
}
public override bool IsSaveLocalMetadataEnabled()
diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs
index 1619dac5ad..cc7d57eb0a 100644
--- a/MediaBrowser.Model/Users/UserPolicy.cs
+++ b/MediaBrowser.Model/Users/UserPolicy.cs
@@ -13,6 +13,7 @@ namespace MediaBrowser.Model.Users
public UserPolicy()
{
IsHidden = true;
+ EnableCollectionManagement = false;
EnableContentDeletion = false;
EnableContentDeletionFromFolders = Array.Empty();
@@ -73,6 +74,12 @@ namespace MediaBrowser.Model.Users
/// true if this instance is hidden; otherwise, false.
public bool IsHidden { get; set; }
+ ///
+ /// Gets or sets a value indicating whether this instance can manage collections.
+ ///
+ /// true if this instance is hidden; otherwise, false.
+ public bool EnableCollectionManagement { get; set; }
+
///
/// Gets or sets a value indicating whether this instance is disabled.
///