aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jellyfin.Server.Implementations/Security/AuthorizationContext.cs57
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs57
2 files changed, 99 insertions, 15 deletions
diff --git a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs
index 659932aca..244abf469 100644
--- a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs
+++ b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs
@@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
-using Jellyfin.Extensions;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using Microsoft.AspNetCore.Http;
@@ -253,29 +252,57 @@ namespace Jellyfin.Server.Implementations.Security
return null;
}
+ // Remove up until the first space
authorizationHeader = authorizationHeader[(firstSpace + 1)..];
+ return GetParts(authorizationHeader);
+ }
- var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ /// <summary>
+ /// Get the authorization header components.
+ /// </summary>
+ /// <param name="authorizationHeader">The authorization header.</param>
+ /// <returns>Dictionary{System.StringSystem.String}.</returns>
+ public static Dictionary<string, string> GetParts(ReadOnlySpan<char> authorizationHeader)
+ {
+ var result = new Dictionary<string, string>();
+ var escaped = false;
+ int start = 0;
+ string key = string.Empty;
- foreach (var item in authorizationHeader.Split(','))
+ int i;
+ for (i = 0; i < authorizationHeader.Length; i++)
{
- var trimmedItem = item.Trim();
- var firstEqualsSign = trimmedItem.IndexOf('=');
-
- if (firstEqualsSign > 0)
+ var token = authorizationHeader[i];
+ if (token == '"' || token == ',')
{
- var key = trimmedItem[..firstEqualsSign].ToString();
- var value = NormalizeValue(trimmedItem[(firstEqualsSign + 1)..].Trim('"').ToString());
- result[key] = value;
+ // Applying a XOR logic to evaluate whether it is opening or closing a value
+ escaped = (!escaped) == (token == '"');
+ if (token == ',' && !escaped)
+ {
+ // Meeting a comma after a closing escape char means the value is complete
+ if (start < i)
+ {
+ result[key] = WebUtility.UrlDecode(authorizationHeader[start..i].Trim('"').ToString());
+ key = string.Empty;
+ }
+
+ start = i + 1;
+ }
+ }
+ else if (!escaped && token == '=')
+ {
+ key = authorizationHeader[start.. i].Trim().ToString();
+ start = i + 1;
}
}
- return result;
- }
+ // Add last value
+ if (start < i)
+ {
+ result[key] = WebUtility.UrlDecode(authorizationHeader[start..i].Trim('"').ToString());
+ }
- private static string NormalizeValue(string value)
- {
- return string.IsNullOrEmpty(value) ? value : WebUtility.HtmlEncode(value);
+ return result;
}
}
}
diff --git a/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs
index a62fd8d5a..23c51999f 100644
--- a/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs
@@ -4,6 +4,7 @@ using AutoFixture;
using AutoFixture.AutoMoq;
using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
using Jellyfin.Api.Constants;
+using Jellyfin.Server.Implementations.Security;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Authorization;
@@ -49,5 +50,61 @@ namespace Jellyfin.Api.Tests.Auth.DefaultAuthorizationPolicy
await _sut.HandleAsync(context);
Assert.True(context.HasSucceeded);
}
+
+ [Theory]
+ [MemberData(nameof(GetParts_ValidAuthHeader_Success_Data))]
+ public void GetParts_ValidAuthHeader_Success(string input, Dictionary<string, string> parts)
+ {
+ var dict = AuthorizationContext.GetParts(input);
+ foreach (var (key, value) in parts)
+ {
+ Assert.Equal(dict[key], value);
+ }
+ }
+
+ private static TheoryData<string, Dictionary<string, string>> GetParts_ValidAuthHeader_Success_Data()
+ {
+ var data = new TheoryData<string, Dictionary<string, string>>();
+
+ data.Add(
+ "x=\"123,123\",y=\"123\"",
+ new Dictionary<string, string>
+ {
+ { "x", "123,123" },
+ { "y", "123" }
+ });
+
+ data.Add(
+ "x=\"123,123\", y=\"123\",z=\"'hi'\"",
+ new Dictionary<string, string>
+ {
+ { "x", "123,123" },
+ { "y", "123" },
+ { "z", "'hi'" }
+ });
+
+ data.Add(
+ "x=\"ab\"",
+ new Dictionary<string, string>
+ {
+ { "x", "ab" }
+ });
+
+ data.Add(
+ "param=Hörbücher",
+ new Dictionary<string, string>
+ {
+ { "param", "Hörbücher" }
+ });
+
+ data.Add(
+ "param=%22%Hörbücher",
+ new Dictionary<string, string>
+ {
+ { "param", "\"%Hörbücher" }
+ });
+
+ return data;
+ }
}
}