mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-02-02 08:28:34 +00:00
Merge branch 'master' into bug/authorization-header-issue
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Controller.Authentication;
|
||||
using MediaBrowser.Controller.Net;
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Security;
|
||||
@@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
|
||||
{
|
||||
if (requestContext.Request.HttpContext.Items.TryGetValue("AuthorizationInfo", out var cached))
|
||||
{
|
||||
return (AuthorizationInfo)cached;
|
||||
return (AuthorizationInfo)cached!; // Cache should never contain null
|
||||
}
|
||||
|
||||
return GetAuthorization(requestContext);
|
||||
@@ -55,15 +55,15 @@ namespace Emby.Server.Implementations.HttpServer.Security
|
||||
}
|
||||
|
||||
private AuthorizationInfo GetAuthorizationInfoFromDictionary(
|
||||
in Dictionary<string, string> auth,
|
||||
in Dictionary<string, string>? auth,
|
||||
in IHeaderDictionary headers,
|
||||
in IQueryCollection queryString)
|
||||
{
|
||||
string deviceId = null;
|
||||
string device = null;
|
||||
string client = null;
|
||||
string version = null;
|
||||
string token = null;
|
||||
string? deviceId = null;
|
||||
string? device = null;
|
||||
string? client = null;
|
||||
string? version = null;
|
||||
string? token = null;
|
||||
|
||||
if (auth != null)
|
||||
{
|
||||
@@ -206,7 +206,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
|
||||
/// </summary>
|
||||
/// <param name="httpReq">The HTTP req.</param>
|
||||
/// <returns>Dictionary{System.StringSystem.String}.</returns>
|
||||
private Dictionary<string, string> GetAuthorizationDictionary(HttpContext httpReq)
|
||||
private Dictionary<string, string>? GetAuthorizationDictionary(HttpContext httpReq)
|
||||
{
|
||||
var auth = httpReq.Request.Headers["X-Emby-Authorization"];
|
||||
|
||||
@@ -215,7 +215,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
|
||||
auth = httpReq.Request.Headers[HeaderNames.Authorization];
|
||||
}
|
||||
|
||||
return GetAuthorization(auth);
|
||||
return GetAuthorization(auth.Count > 0 ? auth[0] : null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -223,7 +223,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
|
||||
/// </summary>
|
||||
/// <param name="httpReq">The HTTP req.</param>
|
||||
/// <returns>Dictionary{System.StringSystem.String}.</returns>
|
||||
private Dictionary<string, string> GetAuthorizationDictionary(HttpRequest httpReq)
|
||||
private Dictionary<string, string>? GetAuthorizationDictionary(HttpRequest httpReq)
|
||||
{
|
||||
var auth = httpReq.Headers["X-Emby-Authorization"];
|
||||
|
||||
@@ -232,7 +232,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
|
||||
auth = httpReq.Headers[HeaderNames.Authorization];
|
||||
}
|
||||
|
||||
return GetAuthorization(auth);
|
||||
return GetAuthorization(auth.Count > 0 ? auth[0] : null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -240,25 +240,25 @@ namespace Emby.Server.Implementations.HttpServer.Security
|
||||
/// </summary>
|
||||
/// <param name="authorizationHeader">The authorization header.</param>
|
||||
/// <returns>Dictionary{System.StringSystem.String}.</returns>
|
||||
private Dictionary<string, string> GetAuthorization(string authorizationHeader)
|
||||
private Dictionary<string, string>? GetAuthorization(ReadOnlySpan<char> authorizationHeader)
|
||||
{
|
||||
if (authorizationHeader == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var parts = authorizationHeader.Split(' ', 2);
|
||||
var firstSpace = authorizationHeader.IndexOf(' ');
|
||||
|
||||
// There should be at least to parts
|
||||
if (parts.Length != 2)
|
||||
// There should be at least two parts
|
||||
if (firstSpace == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var acceptedNames = new[] { "MediaBrowser", "Emby" };
|
||||
var name = authorizationHeader[..firstSpace];
|
||||
|
||||
// It has to be a digest request
|
||||
if (!acceptedNames.Contains(parts[0], StringComparer.OrdinalIgnoreCase))
|
||||
if (!name.Equals("MediaBrowser", StringComparison.OrdinalIgnoreCase)
|
||||
&& !name.Equals("Emby", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
|
||||
var authorization = _authContext.GetAuthorizationInfo(requestContext);
|
||||
|
||||
var user = authorization.User;
|
||||
return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.GetNormalizedRemoteIp(), user);
|
||||
return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.GetNormalizedRemoteIp().ToString(), user);
|
||||
}
|
||||
|
||||
public SessionInfo GetSession(object requestContext)
|
||||
@@ -36,16 +36,16 @@ namespace Emby.Server.Implementations.HttpServer.Security
|
||||
return GetSession((HttpContext)requestContext);
|
||||
}
|
||||
|
||||
public User GetUser(HttpContext requestContext)
|
||||
public User? GetUser(HttpContext requestContext)
|
||||
{
|
||||
var session = GetSession(requestContext);
|
||||
|
||||
return session == null || session.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(session.UserId);
|
||||
}
|
||||
|
||||
public User GetUser(object requestContext)
|
||||
public User? GetUser(object requestContext)
|
||||
{
|
||||
return GetUser((HttpContext)requestContext);
|
||||
return GetUser(((HttpRequest)requestContext).HttpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -55,7 +54,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
RemoteEndPoint = remoteEndPoint;
|
||||
QueryString = query;
|
||||
|
||||
_jsonOptions = JsonDefaults.GetOptions();
|
||||
_jsonOptions = JsonDefaults.Options;
|
||||
LastActivityDate = DateTime.Now;
|
||||
}
|
||||
|
||||
@@ -138,7 +137,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
writer.Advance(bytesRead);
|
||||
|
||||
// Make the data available to the PipeReader
|
||||
FlushResult flushResult = await writer.FlushAsync().ConfigureAwait(false);
|
||||
FlushResult flushResult = await writer.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (flushResult.IsCompleted)
|
||||
{
|
||||
// The PipeReader stopped reading
|
||||
@@ -181,32 +180,16 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
}
|
||||
|
||||
WebSocketMessage<object>? stub;
|
||||
long bytesConsumed = 0;
|
||||
try
|
||||
{
|
||||
|
||||
if (buffer.IsSingleSegment)
|
||||
{
|
||||
stub = JsonSerializer.Deserialize<WebSocketMessage<object>>(buffer.FirstSpan, _jsonOptions);
|
||||
}
|
||||
else
|
||||
{
|
||||
var buf = ArrayPool<byte>.Shared.Rent(Convert.ToInt32(buffer.Length));
|
||||
try
|
||||
{
|
||||
buffer.CopyTo(buf);
|
||||
stub = JsonSerializer.Deserialize<WebSocketMessage<object>>(buf, _jsonOptions);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buf);
|
||||
}
|
||||
}
|
||||
stub = DeserializeWebSocketMessage(buffer, out bytesConsumed);
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
// Tell the PipeReader how much of the buffer we have consumed
|
||||
reader.AdvanceTo(buffer.End);
|
||||
_logger.LogError(ex, "Error processing web socket message");
|
||||
_logger.LogError(ex, "Error processing web socket message: {Data}", Encoding.UTF8.GetString(buffer));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -217,27 +200,34 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
}
|
||||
|
||||
// Tell the PipeReader how much of the buffer we have consumed
|
||||
reader.AdvanceTo(buffer.End);
|
||||
reader.AdvanceTo(buffer.GetPosition(bytesConsumed));
|
||||
|
||||
_logger.LogDebug("WS {IP} received message: {@Message}", RemoteEndPoint, stub);
|
||||
|
||||
var info = new WebSocketMessageInfo
|
||||
{
|
||||
MessageType = stub.MessageType,
|
||||
Data = stub.Data?.ToString(), // Data can be null
|
||||
Connection = this
|
||||
};
|
||||
|
||||
if (info.MessageType == SessionMessageType.KeepAlive)
|
||||
if (stub.MessageType == SessionMessageType.KeepAlive)
|
||||
{
|
||||
await SendKeepAliveResponse().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await OnReceive(info).ConfigureAwait(false);
|
||||
await OnReceive(
|
||||
new WebSocketMessageInfo
|
||||
{
|
||||
MessageType = stub.MessageType,
|
||||
Data = stub.Data?.ToString(), // Data can be null
|
||||
Connection = this
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal WebSocketMessage<object>? DeserializeWebSocketMessage(ReadOnlySequence<byte> bytes, out long bytesConsumed)
|
||||
{
|
||||
var jsonReader = new Utf8JsonReader(bytes);
|
||||
var ret = JsonSerializer.Deserialize<WebSocketMessage<object>>(ref jsonReader, _jsonOptions);
|
||||
bytesConsumed = jsonReader.BytesConsumed;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private Task SendKeepAliveResponse()
|
||||
{
|
||||
LastKeepAliveDate = DateTime.UtcNow;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
@@ -14,15 +16,18 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
public class WebSocketManager : IWebSocketManager
|
||||
{
|
||||
private readonly IWebSocketListener[] _webSocketListeners;
|
||||
private readonly IAuthService _authService;
|
||||
private readonly ILogger<WebSocketManager> _logger;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
public WebSocketManager(
|
||||
IAuthService authService,
|
||||
IEnumerable<IWebSocketListener> webSocketListeners,
|
||||
ILogger<WebSocketManager> logger,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
_webSocketListeners = webSocketListeners.ToArray();
|
||||
_authService = authService;
|
||||
_logger = logger;
|
||||
_loggerFactory = loggerFactory;
|
||||
}
|
||||
@@ -30,6 +35,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||
/// <inheritdoc />
|
||||
public async Task WebSocketRequestHandler(HttpContext context)
|
||||
{
|
||||
_ = _authService.Authenticate(context.Request);
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("WS {IP} request", context.Connection.RemoteIpAddress);
|
||||
|
||||
Reference in New Issue
Block a user