Explorar o código

Allow non-admin users to subscribe to their own Sessions (#13767)

KGT1 hai 5 días
pai
achega
7c6cedd90a

+ 19 - 4
Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+using System.Linq;
 using System.Threading.Tasks;
 using Jellyfin.Data;
 using Jellyfin.Database.Implementations.Enums;
@@ -56,6 +57,21 @@ public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnume
         return Task.FromResult(_sessionManager.Sessions);
     }
 
+    /// <inheritdoc />
+    protected override Task<IEnumerable<SessionInfo>> GetDataToSendForConnection(IWebSocketConnection connection)
+    {
+        // For non-admin users, filter the sessions to only include their own sessions
+        if (connection.AuthorizationInfo?.User is not null &&
+            !connection.AuthorizationInfo.IsApiKey &&
+            !connection.AuthorizationInfo.User.HasPermission(PermissionKind.IsAdministrator))
+        {
+            var userId = connection.AuthorizationInfo.User.Id;
+            return Task.FromResult(_sessionManager.Sessions.Where(s => s.UserId.Equals(userId) || s.ContainsUser(userId)));
+        }
+
+        return Task.FromResult(_sessionManager.Sessions);
+    }
+
     /// <inheritdoc />
     protected override async ValueTask DisposeAsyncCore()
     {
@@ -80,11 +96,10 @@ public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnume
     /// <param name="message">The message.</param>
     protected override void Start(WebSocketMessageInfo message)
     {
-        if (!message.Connection.AuthorizationInfo.IsApiKey
-            && (message.Connection.AuthorizationInfo.User is null
-                || !message.Connection.AuthorizationInfo.User.HasPermission(PermissionKind.IsAdministrator)))
+        // Allow all authenticated users to subscribe to session information
+        if (message.Connection.AuthorizationInfo.User is null && !message.Connection.AuthorizationInfo.IsApiKey)
         {
-            throw new AuthenticationException("Only admin users can subscribe to session information.");
+            throw new AuthenticationException("User must be authenticated to subscribe to session Information.");
         }
 
         base.Start(message);

+ 19 - 8
MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs

@@ -80,6 +80,16 @@ namespace MediaBrowser.Controller.Net
         /// <returns>Task{`1}.</returns>
         protected abstract Task<TReturnDataType> GetDataToSend();
 
+        /// <summary>
+        /// Gets the data to send for a specific connection.
+        /// </summary>
+        /// <param name="connection">The connection.</param>
+        /// <returns>Task{`1}.</returns>
+        protected virtual Task<TReturnDataType> GetDataToSendForConnection(IWebSocketConnection connection)
+        {
+            return GetDataToSend();
+        }
+
         /// <summary>
         /// Processes the message.
         /// </summary>
@@ -174,17 +184,11 @@ namespace MediaBrowser.Controller.Net
                             continue;
                         }
 
-                        var data = await GetDataToSend().ConfigureAwait(false);
-                        if (data is null)
-                        {
-                            continue;
-                        }
-
                         IEnumerable<Task> GetTasks()
                         {
                             foreach (var tuple in tuples)
                             {
-                                yield return SendDataInternal(data, tuple);
+                                yield return SendDataForConnectionAsync(tuple);
                             }
                         }
 
@@ -198,12 +202,19 @@ namespace MediaBrowser.Controller.Net
             }
         }
 
-        private async Task SendDataInternal(TReturnDataType data, (IWebSocketConnection Connection, CancellationTokenSource CancellationTokenSource, TStateType State) tuple)
+        private async Task SendDataForConnectionAsync((IWebSocketConnection Connection, CancellationTokenSource CancellationTokenSource, TStateType State) tuple)
         {
             try
             {
                 var (connection, cts, state) = tuple;
                 var cancellationToken = cts.Token;
+
+                var data = await GetDataToSendForConnection(connection).ConfigureAwait(false);
+                if (data is null)
+                {
+                    return;
+                }
+
                 await connection.SendAsync(
                     new OutboundWebSocketMessage<TReturnDataType> { MessageType = Type, Data = data },
                     cancellationToken).ConfigureAwait(false);