From 958f8f71e8174aa3884cbafa54d07bf247517014 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Thu, 8 Jun 2023 10:36:04 +0200 Subject: [PATCH 1/7] Add wrapper object for authentication event information --- .../Session/SessionManager.cs | 5 +- .../Security/AuthenticationFailedLogger.cs | 6 +- .../Security/AuthenticationSucceededLogger.cs | 8 +-- .../EventingServiceCollectionExtensions.cs | 7 +-- .../AuthenticationRequestEventArgs.cs | 60 +++++++++++++++++++ .../AuthenticationResultEventArgs.cs | 37 ++++++++++++ 6 files changed, 110 insertions(+), 13 deletions(-) create mode 100644 MediaBrowser.Controller/Events/Authentication/AuthenticationRequestEventArgs.cs create mode 100644 MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 5f6dc93fb3..f26cc56636 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -24,6 +24,7 @@ using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Events; +using MediaBrowser.Controller.Events.Authentication; using MediaBrowser.Controller.Events.Session; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; @@ -1462,7 +1463,7 @@ namespace Emby.Server.Implementations.Session if (user is null) { - await _eventManager.PublishAsync(new GenericEventArgs(request)).ConfigureAwait(false); + await _eventManager.PublishAsync(new GenericEventArgs(new AuthenticationRequestEventArgs(request))).ConfigureAwait(false); throw new AuthenticationException("Invalid username or password entered."); } @@ -1498,7 +1499,7 @@ namespace Emby.Server.Implementations.Session ServerId = _appHost.SystemId }; - await _eventManager.PublishAsync(new GenericEventArgs(returnResult)).ConfigureAwait(false); + await _eventManager.PublishAsync(new GenericEventArgs(new AuthenticationResultEventArgs(returnResult))).ConfigureAwait(false); return returnResult; } diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs index f899b4497a..f4835a585f 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Events; using MediaBrowser.Controller.Events; -using MediaBrowser.Controller.Session; +using MediaBrowser.Controller.Events.Authentication; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Globalization; using Microsoft.Extensions.Logging; @@ -14,7 +14,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security /// /// Creates an entry in the activity log when there is a failed login attempt. /// - public class AuthenticationFailedLogger : IEventConsumer> + public class AuthenticationFailedLogger : IEventConsumer> { private readonly ILocalizationManager _localizationManager; private readonly IActivityManager _activityManager; @@ -31,7 +31,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security } /// - public async Task OnEvent(GenericEventArgs eventArgs) + public async Task OnEvent(GenericEventArgs eventArgs) { await _activityManager.CreateAsync(new ActivityLog( string.Format( diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs index 8b0bd84c66..fe2737bafc 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs @@ -2,8 +2,8 @@ using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Events; -using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Events; +using MediaBrowser.Controller.Events.Authentication; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Globalization; @@ -12,7 +12,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security /// /// Creates an entry in the activity log when there is a successful login attempt. /// - public class AuthenticationSucceededLogger : IEventConsumer> + public class AuthenticationSucceededLogger : IEventConsumer> { private readonly ILocalizationManager _localizationManager; private readonly IActivityManager _activityManager; @@ -29,7 +29,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security } /// - public async Task OnEvent(GenericEventArgs eventArgs) + public async Task OnEvent(GenericEventArgs eventArgs) { await _activityManager.CreateAsync(new ActivityLog( string.Format( @@ -42,7 +42,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security ShortOverview = string.Format( CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("LabelIpAddressValue"), - eventArgs.Argument.SessionInfo.RemoteEndPoint), + eventArgs.Argument.SessionInfo?.RemoteEndPoint), }).ConfigureAwait(false); } } diff --git a/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs b/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs index 5d558189b1..93ab15e24b 100644 --- a/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs +++ b/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs @@ -8,12 +8,11 @@ using Jellyfin.Server.Implementations.Events.Consumers.System; using Jellyfin.Server.Implementations.Events.Consumers.Updates; using Jellyfin.Server.Implementations.Events.Consumers.Users; using MediaBrowser.Common.Updates; -using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Events; +using MediaBrowser.Controller.Events.Authentication; using MediaBrowser.Controller.Events.Session; using MediaBrowser.Controller.Events.Updates; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -35,8 +34,8 @@ namespace Jellyfin.Server.Implementations.Events collection.AddScoped, SubtitleDownloadFailureLogger>(); // Security consumers - collection.AddScoped>, AuthenticationFailedLogger>(); - collection.AddScoped>, AuthenticationSucceededLogger>(); + collection.AddScoped>, AuthenticationFailedLogger>(); + collection.AddScoped>, AuthenticationSucceededLogger>(); // Session consumers collection.AddScoped, PlaybackStartLogger>(); diff --git a/MediaBrowser.Controller/Events/Authentication/AuthenticationRequestEventArgs.cs b/MediaBrowser.Controller/Events/Authentication/AuthenticationRequestEventArgs.cs new file mode 100644 index 0000000000..c0d8a277bd --- /dev/null +++ b/MediaBrowser.Controller/Events/Authentication/AuthenticationRequestEventArgs.cs @@ -0,0 +1,60 @@ +using System; +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.Events.Authentication; + +/// +/// A class representing an authentication result event. +/// +public class AuthenticationRequestEventArgs +{ + /// + /// Initializes a new instance of the class. + /// + /// The . + public AuthenticationRequestEventArgs(AuthenticationRequest request) + { + Username = request.Username; + UserId = request.UserId; + App = request.App; + AppVersion = request.AppVersion; + DeviceId = request.DeviceId; + DeviceName = request.DeviceName; + RemoteEndPoint = request.RemoteEndPoint; + } + + /// + /// Gets or sets the user name. + /// + public string? Username { get; set; } + + /// + /// Gets or sets the user id. + /// + public Guid? UserId { get; set; } + + /// + /// Gets or sets the app. + /// + public string? App { get; set; } + + /// + /// Gets or sets the app version. + /// + public string? AppVersion { get; set; } + + /// + /// Gets or sets the device id. + /// + public string? DeviceId { get; set; } + + /// + /// Gets or sets the device name. + /// + public string? DeviceName { get; set; } + + /// + /// Gets or sets the remote endpoint. + /// + public string? RemoteEndPoint { get; set; } +} diff --git a/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs b/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs new file mode 100644 index 0000000000..5564fa1cef --- /dev/null +++ b/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs @@ -0,0 +1,37 @@ +using MediaBrowser.Controller.Authentication; +using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Dto; + +namespace MediaBrowser.Controller.Events.Authentication; + +/// +/// A class representing an authentication result event. +/// +public class AuthenticationResultEventArgs +{ + /// + /// Initializes a new instance of the class. + /// + /// The . + public AuthenticationResultEventArgs(AuthenticationResult result) + { + User = result.User; + SessionInfo = result.SessionInfo; + ServerId = result.ServerId; + } + + /// + /// Gets or sets the user. + /// + public UserDto User { get; set; } + + /// + /// Gets or sets the session information. + /// + public SessionInfo? SessionInfo { get; set; } + + /// + /// Gets or sets the server id. + /// + public string? ServerId { get; set; } +} From 46a6755e65c9587fd1ae33ee4ffdb3cd406fd72b Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Thu, 8 Jun 2023 10:36:45 +0200 Subject: [PATCH 2/7] Add item id to playback start/stop events --- .../Consumers/Session/PlaybackStartLogger.cs | 21 +++++++++++-------- .../Consumers/Session/PlaybackStopLogger.cs | 5 ++++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs index aeb62e814c..8ed76565cb 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs @@ -58,15 +58,18 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session var user = eventArgs.Users[0]; await _activityManager.CreateAsync(new ActivityLog( - string.Format( - CultureInfo.InvariantCulture, - _localizationManager.GetLocalizedString("UserStartedPlayingItemWithValues"), - user.Username, - GetItemName(eventArgs.MediaInfo), - eventArgs.DeviceName), - GetPlaybackNotificationType(eventArgs.MediaInfo.MediaType), - user.Id)) - .ConfigureAwait(false); + string.Format( + CultureInfo.InvariantCulture, + _localizationManager.GetLocalizedString("UserStartedPlayingItemWithValues"), + user.Username, + GetItemName(eventArgs.MediaInfo), + eventArgs.DeviceName), + GetPlaybackNotificationType(eventArgs.MediaInfo.MediaType), + user.Id) + { + ItemId = eventArgs.Item?.Id.ToString() + }) + .ConfigureAwait(false); } private static string GetItemName(BaseItemDto item) diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs index dd7290fb84..da9c2b8a2f 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs @@ -73,7 +73,10 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session GetItemName(item), eventArgs.DeviceName), notificationType, - user.Id)) + user.Id) + { + ItemId = eventArgs.Item?.Id.ToString() + }) .ConfigureAwait(false); } From 05d98fe24c594ae43de4cd9f54139f8b04324119 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 9 Jun 2023 17:11:22 +0200 Subject: [PATCH 3/7] Enforce permissions on websocket connections --- .../HttpServer/WebSocketConnection.cs | 28 ++++++++----------- .../HttpServer/WebSocketManager.cs | 3 +- .../ActivityLogWebSocketListener.cs | 14 +++++++++- .../SessionInfoWebSocketListener.cs | 12 ++++++++ .../Net/BasePeriodicWebSocketListener.cs | 2 +- .../Net/IWebSocketConnection.cs | 17 +++++++++-- .../HttpServer/WebSocketConnectionTests.cs | 8 +++--- 7 files changed, 58 insertions(+), 26 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index fd7653a32d..7f620d666d 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -12,6 +12,7 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net.WebSocketMessages; using MediaBrowser.Controller.Net.WebSocketMessages.Outbound; using MediaBrowser.Model.Session; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.HttpServer @@ -43,14 +44,17 @@ namespace Emby.Server.Implementations.HttpServer /// /// The logger. /// The socket. + /// The authorization information. /// The remote end point. public WebSocketConnection( ILogger logger, WebSocket socket, + AuthorizationInfo authorizationInfo, IPAddress? remoteEndPoint) { _logger = logger; _socket = socket; + AuthorizationInfo = authorizationInfo; RemoteEndPoint = remoteEndPoint; _jsonOptions = JsonDefaults.Options; @@ -60,30 +64,22 @@ namespace Emby.Server.Implementations.HttpServer /// public event EventHandler? Closed; - /// - /// Gets the remote end point. - /// + /// + public AuthorizationInfo AuthorizationInfo { get; } + + /// public IPAddress? RemoteEndPoint { get; } - /// - /// Gets or sets the receive action. - /// - /// The receive action. + /// public Func? OnReceive { get; set; } - /// - /// Gets the last activity date. - /// - /// The last activity date. + /// public DateTime LastActivityDate { get; private set; } /// public DateTime LastKeepAliveDate { get; set; } - /// - /// Gets the state. - /// - /// The state. + /// public WebSocketState State => _socket.State; /// @@ -101,7 +97,7 @@ namespace Emby.Server.Implementations.HttpServer } /// - public async Task ProcessAsync(CancellationToken cancellationToken = default) + public async Task ReceiveAsync(CancellationToken cancellationToken = default) { var pipe = new Pipe(); var writer = pipe.Writer; diff --git a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs index ecfb242f6f..52f14b0b10 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs @@ -51,6 +51,7 @@ namespace Emby.Server.Implementations.HttpServer using var connection = new WebSocketConnection( _loggerFactory.CreateLogger(), webSocket, + authorizationInfo, context.GetNormalizedRemoteIP()) { OnReceive = ProcessWebSocketMessageReceived @@ -64,7 +65,7 @@ namespace Emby.Server.Implementations.HttpServer await Task.WhenAll(tasks).ConfigureAwait(false); - await connection.ProcessAsync().ConfigureAwait(false); + await connection.ReceiveAsync().ConfigureAwait(false); _logger.LogInformation("WS {IP} closed", context.Connection.RemoteIpAddress); } catch (Exception ex) // Otherwise ASP.Net will ignore the exception diff --git a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs index 4a5e0ecd4f..33d391f296 100644 --- a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs @@ -1,6 +1,8 @@ using System; using System.Threading.Tasks; +using Jellyfin.Data.Enums; using Jellyfin.Data.Events; +using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Session; @@ -9,7 +11,7 @@ using Microsoft.Extensions.Logging; namespace Jellyfin.Api.WebSocketListeners; /// -/// Class SessionInfoWebSocketListener. +/// Class ActivityLogWebSocketListener. /// public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener { @@ -56,6 +58,16 @@ public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener e) { await SendData(true).ConfigureAwait(false); diff --git a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs index 0d8bf205c9..0d614ba4f2 100644 --- a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; +using Jellyfin.Data.Enums; +using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; @@ -66,6 +68,16 @@ public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener /// The message. - private void Start(WebSocketMessageInfo message) + protected void Start(WebSocketMessageInfo message) { var vals = message.Data.Split(','); diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs index 79f0846b4a..bba5a6b851 100644 --- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Net; using System.Net.WebSockets; @@ -9,6 +7,9 @@ using MediaBrowser.Controller.Net.WebSocketMessages; namespace MediaBrowser.Controller.Net { + /// + /// Interface for WebSocket connections. + /// public interface IWebSocketConnection : IAsyncDisposable, IDisposable { /// @@ -40,6 +41,11 @@ namespace MediaBrowser.Controller.Net /// The state. WebSocketState State { get; } + /// + /// Gets the authorization information. + /// + public AuthorizationInfo AuthorizationInfo { get; } + /// /// Gets the remote end point. /// @@ -65,6 +71,11 @@ namespace MediaBrowser.Controller.Net /// The message is null. Task SendAsync(OutboundWebSocketMessage message, CancellationToken cancellationToken); - Task ProcessAsync(CancellationToken cancellationToken = default); + /// + /// Receives a message asynchronously. + /// + /// The cancellation token. + /// Task. + Task ReceiveAsync(CancellationToken cancellationToken = default); } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/HttpServer/WebSocketConnectionTests.cs b/tests/Jellyfin.Server.Implementations.Tests/HttpServer/WebSocketConnectionTests.cs index f016118192..22667ee82d 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/HttpServer/WebSocketConnectionTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/HttpServer/WebSocketConnectionTests.cs @@ -13,7 +13,7 @@ namespace Jellyfin.Server.Implementations.Tests.HttpServer [Fact] public void DeserializeWebSocketMessage_SingleSegment_Success() { - var con = new WebSocketConnection(new NullLogger(), null!, null!); + var con = new WebSocketConnection(new NullLogger(), null!, null!, null!); var bytes = File.ReadAllBytes("Test Data/HttpServer/ForceKeepAlive.json"); con.DeserializeWebSocketMessage(new ReadOnlySequence(bytes), out var bytesConsumed); Assert.Equal(109, bytesConsumed); @@ -23,7 +23,7 @@ namespace Jellyfin.Server.Implementations.Tests.HttpServer public void DeserializeWebSocketMessage_MultipleSegments_Success() { const int SplitPos = 64; - var con = new WebSocketConnection(new NullLogger(), null!, null!); + var con = new WebSocketConnection(new NullLogger(), null!, null!, null!); var bytes = File.ReadAllBytes("Test Data/HttpServer/ForceKeepAlive.json"); var seg1 = new BufferSegment(new Memory(bytes, 0, SplitPos)); var seg2 = seg1.Append(new Memory(bytes, SplitPos, bytes.Length - SplitPos)); @@ -34,7 +34,7 @@ namespace Jellyfin.Server.Implementations.Tests.HttpServer [Fact] public void DeserializeWebSocketMessage_ValidPartial_Success() { - var con = new WebSocketConnection(new NullLogger(), null!, null!); + var con = new WebSocketConnection(new NullLogger(), null!, null!, null!); var bytes = File.ReadAllBytes("Test Data/HttpServer/ValidPartial.json"); con.DeserializeWebSocketMessage(new ReadOnlySequence(bytes), out var bytesConsumed); Assert.Equal(109, bytesConsumed); @@ -43,7 +43,7 @@ namespace Jellyfin.Server.Implementations.Tests.HttpServer [Fact] public void DeserializeWebSocketMessage_Partial_ThrowJsonException() { - var con = new WebSocketConnection(new NullLogger(), null!, null!); + var con = new WebSocketConnection(new NullLogger(), null!, null!, null!); var bytes = File.ReadAllBytes("Test Data/HttpServer/Partial.json"); Assert.Throws(() => con.DeserializeWebSocketMessage(new ReadOnlySequence(bytes), out var bytesConsumed)); } From a0d13a241891bbf832cc86909f0dbe20c979be52 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 10 Jun 2023 18:06:22 +0200 Subject: [PATCH 4/7] Apply suggestions from code review Co-authored-by: Cody Robibero --- .../Events/Consumers/Session/PlaybackStartLogger.cs | 2 +- .../Events/Consumers/Session/PlaybackStopLogger.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs index 8ed76565cb..27726a57a6 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs @@ -67,7 +67,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session GetPlaybackNotificationType(eventArgs.MediaInfo.MediaType), user.Id) { - ItemId = eventArgs.Item?.Id.ToString() + ItemId = eventArgs.Item?.Id.ToString("N", CultureInfo.InvariantCulture), }) .ConfigureAwait(false); } diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs index da9c2b8a2f..6b16477aa7 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs @@ -75,7 +75,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session notificationType, user.Id) { - ItemId = eventArgs.Item?.Id.ToString() + ItemId = eventArgs.Item?.Id.ToString("N", CultureInfo.InvariantCulture), }) .ConfigureAwait(false); } From 266d55b7aab84133bdc66c28b628f2ddd4ad7c9e Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 7 Jul 2023 08:52:14 +0200 Subject: [PATCH 5/7] Fix bad string interpolation in MaskToCidr --- Jellyfin.Networking/Extensions/NetworkExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Networking/Extensions/NetworkExtensions.cs b/Jellyfin.Networking/Extensions/NetworkExtensions.cs index d55f78135b..e45fa3bcb7 100644 --- a/Jellyfin.Networking/Extensions/NetworkExtensions.cs +++ b/Jellyfin.Networking/Extensions/NetworkExtensions.cs @@ -104,7 +104,7 @@ public static partial class NetworkExtensions Span bytes = stackalloc byte[mask.AddressFamily == AddressFamily.InterNetwork ? Network.IPv4MaskBytes : Network.IPv6MaskBytes]; if (!mask.TryWriteBytes(bytes, out var bytesWritten)) { - Console.WriteLine("Unable to write address bytes, only {bytesWritten} bytes written."); + Console.WriteLine("Unable to write address bytes, only ${bytesWritten} bytes written."); } var zeroed = false; From 368f9202ce0048b92b3b43ea78a96486f9cd23c4 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 15 Jul 2023 20:14:31 +0200 Subject: [PATCH 6/7] Apply review suggestions --- .../WebSocketListeners/ActivityLogWebSocketListener.cs | 6 +++++- .../WebSocketListeners/SessionInfoWebSocketListener.cs | 6 +++++- .../Net/BasePeriodicWebSocketListener.cs | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs index 33d391f296..5b90d65d84 100644 --- a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs @@ -58,7 +58,11 @@ public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener + /// Starts sending messages over an activity log web socket. + /// + /// The message. + protected override void Start(WebSocketMessageInfo message) { if (!message.Connection.AuthorizationInfo.User.HasPermission(PermissionKind.IsAdministrator)) { diff --git a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs index 0d614ba4f2..b403ff46d0 100644 --- a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs @@ -68,7 +68,11 @@ public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener + /// Starts sending messages over a session info web socket. + /// + /// The message. + protected override void Start(WebSocketMessageInfo message) { if (!message.Connection.AuthorizationInfo.User.HasPermission(PermissionKind.IsAdministrator)) { diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index 0ad1e4f50b..e0942e490b 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -96,7 +96,7 @@ namespace MediaBrowser.Controller.Net /// Starts sending messages over a web socket. /// /// The message. - protected void Start(WebSocketMessageInfo message) + protected virtual void Start(WebSocketMessageInfo message) { var vals = message.Data.Split(','); From 4bb17039d70683e8a159db92823fed6d992e2fe4 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 29 Jul 2023 13:50:55 +0200 Subject: [PATCH 7/7] Apply review suggestions --- Emby.Server.Implementations/Session/SessionManager.cs | 4 ++-- .../Consumers/Security/AuthenticationFailedLogger.cs | 9 ++++----- .../Security/AuthenticationSucceededLogger.cs | 10 +++++----- .../Events/EventingServiceCollectionExtensions.cs | 4 ++-- .../Authentication/AuthenticationRequestEventArgs.cs | 2 +- .../Authentication/AuthenticationResultEventArgs.cs | 3 ++- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index f26cc56636..03ff96b19a 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1463,7 +1463,7 @@ namespace Emby.Server.Implementations.Session if (user is null) { - await _eventManager.PublishAsync(new GenericEventArgs(new AuthenticationRequestEventArgs(request))).ConfigureAwait(false); + await _eventManager.PublishAsync(new AuthenticationRequestEventArgs(request)).ConfigureAwait(false); throw new AuthenticationException("Invalid username or password entered."); } @@ -1499,7 +1499,7 @@ namespace Emby.Server.Implementations.Session ServerId = _appHost.SystemId }; - await _eventManager.PublishAsync(new GenericEventArgs(new AuthenticationResultEventArgs(returnResult))).ConfigureAwait(false); + await _eventManager.PublishAsync(new AuthenticationResultEventArgs(returnResult)).ConfigureAwait(false); return returnResult; } diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs index f4835a585f..b5f18d9834 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs @@ -2,7 +2,6 @@ using System.Globalization; using System.Threading.Tasks; using Jellyfin.Data.Entities; -using Jellyfin.Data.Events; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Events.Authentication; using MediaBrowser.Model.Activity; @@ -14,7 +13,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security /// /// Creates an entry in the activity log when there is a failed login attempt. /// - public class AuthenticationFailedLogger : IEventConsumer> + public class AuthenticationFailedLogger : IEventConsumer { private readonly ILocalizationManager _localizationManager; private readonly IActivityManager _activityManager; @@ -31,13 +30,13 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security } /// - public async Task OnEvent(GenericEventArgs eventArgs) + public async Task OnEvent(AuthenticationRequestEventArgs eventArgs) { await _activityManager.CreateAsync(new ActivityLog( string.Format( CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("FailedLoginAttemptWithUserName"), - eventArgs.Argument.Username), + eventArgs.Username), "AuthenticationFailed", Guid.Empty) { @@ -45,7 +44,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security ShortOverview = string.Format( CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("LabelIpAddressValue"), - eventArgs.Argument.RemoteEndPoint), + eventArgs.RemoteEndPoint), }).ConfigureAwait(false); } } diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs index fe2737bafc..2ee5b4e88d 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs @@ -12,7 +12,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security /// /// Creates an entry in the activity log when there is a successful login attempt. /// - public class AuthenticationSucceededLogger : IEventConsumer> + public class AuthenticationSucceededLogger : IEventConsumer { private readonly ILocalizationManager _localizationManager; private readonly IActivityManager _activityManager; @@ -29,20 +29,20 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Security } /// - public async Task OnEvent(GenericEventArgs eventArgs) + public async Task OnEvent(AuthenticationResultEventArgs eventArgs) { await _activityManager.CreateAsync(new ActivityLog( string.Format( CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("AuthenticationSucceededWithUserName"), - eventArgs.Argument.User.Name), + eventArgs.User.Name), "AuthenticationSucceeded", - eventArgs.Argument.User.Id) + eventArgs.User.Id) { ShortOverview = string.Format( CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("LabelIpAddressValue"), - eventArgs.Argument.SessionInfo?.RemoteEndPoint), + eventArgs.SessionInfo?.RemoteEndPoint), }).ConfigureAwait(false); } } diff --git a/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs b/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs index 93ab15e24b..9a473de52d 100644 --- a/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs +++ b/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs @@ -34,8 +34,8 @@ namespace Jellyfin.Server.Implementations.Events collection.AddScoped, SubtitleDownloadFailureLogger>(); // Security consumers - collection.AddScoped>, AuthenticationFailedLogger>(); - collection.AddScoped>, AuthenticationSucceededLogger>(); + collection.AddScoped, AuthenticationFailedLogger>(); + collection.AddScoped, AuthenticationSucceededLogger>(); // Session consumers collection.AddScoped, PlaybackStartLogger>(); diff --git a/MediaBrowser.Controller/Events/Authentication/AuthenticationRequestEventArgs.cs b/MediaBrowser.Controller/Events/Authentication/AuthenticationRequestEventArgs.cs index c0d8a277bd..2143c69986 100644 --- a/MediaBrowser.Controller/Events/Authentication/AuthenticationRequestEventArgs.cs +++ b/MediaBrowser.Controller/Events/Authentication/AuthenticationRequestEventArgs.cs @@ -6,7 +6,7 @@ namespace MediaBrowser.Controller.Events.Authentication; /// /// A class representing an authentication result event. /// -public class AuthenticationRequestEventArgs +public class AuthenticationRequestEventArgs : EventArgs { /// /// Initializes a new instance of the class. diff --git a/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs b/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs index 5564fa1cef..357ef9406d 100644 --- a/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs +++ b/MediaBrowser.Controller/Events/Authentication/AuthenticationResultEventArgs.cs @@ -1,3 +1,4 @@ +using System; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; @@ -7,7 +8,7 @@ namespace MediaBrowser.Controller.Events.Authentication; /// /// A class representing an authentication result event. /// -public class AuthenticationResultEventArgs +public class AuthenticationResultEventArgs : EventArgs { /// /// Initializes a new instance of the class.