diff options
Diffstat (limited to 'Jellyfin.Server')
| -rw-r--r-- | Jellyfin.Server/Filters/SecurityRequirementsOperationFilter.cs | 132 | ||||
| -rw-r--r-- | Jellyfin.Server/Helpers/StartupHelpers.cs | 3 | ||||
| -rw-r--r-- | Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs | 6 | ||||
| -rw-r--r-- | Jellyfin.Server/Program.cs | 11 |
4 files changed, 89 insertions, 63 deletions
diff --git a/Jellyfin.Server/Filters/SecurityRequirementsOperationFilter.cs b/Jellyfin.Server/Filters/SecurityRequirementsOperationFilter.cs index fb9f6d0a6..fb0bd817c 100644 --- a/Jellyfin.Server/Filters/SecurityRequirementsOperationFilter.cs +++ b/Jellyfin.Server/Filters/SecurityRequirementsOperationFilter.cs @@ -1,91 +1,105 @@ using System; using System.Collections.Generic; using System.Linq; +using Jellyfin.Api.Auth.DefaultAuthorizationPolicy; using Jellyfin.Api.Constants; +using Jellyfin.Extensions; using Microsoft.AspNetCore.Authorization; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; -namespace Jellyfin.Server.Filters +namespace Jellyfin.Server.Filters; + +/// <summary> +/// Security requirement operation filter. +/// </summary> +public class SecurityRequirementsOperationFilter : IOperationFilter { + private const string DefaultAuthPolicy = "DefaultAuthorization"; + private static readonly Type _attributeType = typeof(AuthorizeAttribute); + + private readonly IAuthorizationPolicyProvider _authorizationPolicyProvider; + /// <summary> - /// Security requirement operation filter. + /// Initializes a new instance of the <see cref="SecurityRequirementsOperationFilter"/> class. /// </summary> - public class SecurityRequirementsOperationFilter : IOperationFilter + /// <param name="authorizationPolicyProvider">The authorization policy provider.</param> + public SecurityRequirementsOperationFilter(IAuthorizationPolicyProvider authorizationPolicyProvider) { - /// <inheritdoc /> - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - var requiredScopes = new List<string>(); + _authorizationPolicyProvider = authorizationPolicyProvider; + } - var requiresAuth = false; - // Add all method scopes. - foreach (var attribute in context.MethodInfo.GetCustomAttributes(true)) - { - if (attribute is not AuthorizeAttribute authorizeAttribute) - { - continue; - } + /// <inheritdoc /> + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + var requiredScopes = new List<string>(); - requiresAuth = true; - if (authorizeAttribute.Policy is not null - && !requiredScopes.Contains(authorizeAttribute.Policy, StringComparer.Ordinal)) - { - requiredScopes.Add(authorizeAttribute.Policy); - } + var requiresAuth = false; + // Add all method scopes. + foreach (var authorizeAttribute in context.MethodInfo.GetCustomAttributes(_attributeType, true).Cast<AuthorizeAttribute>()) + { + requiresAuth = true; + var policy = authorizeAttribute.Policy ?? DefaultAuthPolicy; + if (!requiredScopes.Contains(policy, StringComparer.Ordinal)) + { + requiredScopes.Add(policy); } + } - // Add controller scopes if any. - var controllerAttributes = context.MethodInfo.DeclaringType?.GetCustomAttributes(true); - if (controllerAttributes is not null) + // Add controller scopes if any. + var controllerAttributes = context.MethodInfo.DeclaringType?.GetCustomAttributes(_attributeType, true).Cast<AuthorizeAttribute>(); + if (controllerAttributes is not null) + { + foreach (var authorizeAttribute in controllerAttributes) { - foreach (var attribute in controllerAttributes) + requiresAuth = true; + var policy = authorizeAttribute.Policy ?? DefaultAuthPolicy; + if (!requiredScopes.Contains(policy, StringComparer.Ordinal)) { - if (attribute is not AuthorizeAttribute authorizeAttribute) - { - continue; - } - - requiresAuth = true; - if (authorizeAttribute.Policy is not null - && !requiredScopes.Contains(authorizeAttribute.Policy, StringComparer.Ordinal)) - { - requiredScopes.Add(authorizeAttribute.Policy); - } + requiredScopes.Add(policy); } } + } - if (!requiresAuth) - { - return; - } + if (!requiresAuth) + { + return; + } - if (!operation.Responses.ContainsKey("401")) - { - operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" }); - } + if (!operation.Responses.ContainsKey("401")) + { + operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" }); + } - if (!operation.Responses.ContainsKey("403")) - { - operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" }); - } + if (!operation.Responses.ContainsKey("403")) + { + operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" }); + } - var scheme = new OpenApiSecurityScheme + var scheme = new OpenApiSecurityScheme + { + Reference = new OpenApiReference { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = AuthenticationSchemes.CustomAuthentication - } - }; + Type = ReferenceType.SecurityScheme, + Id = AuthenticationSchemes.CustomAuthentication + }, + }; - operation.Security = new List<OpenApiSecurityRequirement> + // Add DefaultAuthorization scope to any endpoint that has a policy with a requirement that is a subset of DefaultAuthorization. + if (!requiredScopes.Contains(DefaultAuthPolicy.AsSpan(), StringComparison.Ordinal)) + { + foreach (var scope in requiredScopes) { - new OpenApiSecurityRequirement + var authorizationPolicy = _authorizationPolicyProvider.GetPolicyAsync(scope).GetAwaiter().GetResult(); + if (authorizationPolicy is not null + && authorizationPolicy.Requirements.Any(r => r is DefaultAuthorizationRequirement)) { - [scheme] = requiredScopes + requiredScopes.Add(DefaultAuthPolicy); + break; } - }; + } } + + operation.Security = [new OpenApiSecurityRequirement { [scheme] = requiredScopes }]; } } diff --git a/Jellyfin.Server/Helpers/StartupHelpers.cs b/Jellyfin.Server/Helpers/StartupHelpers.cs index 66d393dec..5311a30e4 100644 --- a/Jellyfin.Server/Helpers/StartupHelpers.cs +++ b/Jellyfin.Server/Helpers/StartupHelpers.cs @@ -57,6 +57,9 @@ public static class StartupHelpers logger.LogInformation("User Interactive: {IsUserInteractive}", Environment.UserInteractive); logger.LogInformation("Processor count: {ProcessorCount}", Environment.ProcessorCount); logger.LogInformation("Program data path: {ProgramDataPath}", appPaths.ProgramDataPath); + logger.LogInformation("Log directory path: {LogDirectoryPath}", appPaths.LogDirectoryPath); + logger.LogInformation("Config directory path: {ConfigurationDirectoryPath}", appPaths.ConfigurationDirectoryPath); + logger.LogInformation("Cache path: {CachePath}", appPaths.CachePath); logger.LogInformation("Web resources path: {WebPath}", appPaths.WebPath); logger.LogInformation("Application directory: {ApplicationPath}", appPaths.ProgramSystemPath); } diff --git a/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs b/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs index cf3182003..3655a610d 100644 --- a/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs +++ b/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs @@ -54,12 +54,12 @@ internal class FixPlaylistOwner : IMigrationRoutine foreach (var playlist in playlists) { var shares = playlist.Shares; - if (shares.Length > 0) + if (shares.Count > 0) { var firstEditShare = shares.First(x => x.CanEdit); - if (firstEditShare is not null && Guid.TryParse(firstEditShare.UserId, out var guid)) + if (firstEditShare is not null) { - playlist.OwnerUserId = guid; + playlist.OwnerUserId = firstEditShare.UserId; playlist.Shares = shares.Where(x => x != firstEditShare).ToArray(); playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult(); _playlistManager.SavePlaylistFile(playlist); diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index c70ef1719..fd7696906 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -12,6 +12,7 @@ using Jellyfin.Server.Helpers; using Jellyfin.Server.Implementations; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller; +using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -139,7 +140,15 @@ namespace Jellyfin.Server host = Host.CreateDefaultBuilder() .UseConsoleLifetime() .ConfigureServices(services => appHost.Init(services)) - .ConfigureWebHostDefaults(webHostBuilder => webHostBuilder.ConfigureWebHostBuilder(appHost, startupConfig, appPaths, _logger)) + .ConfigureWebHostDefaults(webHostBuilder => + { + webHostBuilder.ConfigureWebHostBuilder(appHost, startupConfig, appPaths, _logger); + if (bool.TryParse(Environment.GetEnvironmentVariable("JELLYFIN_ENABLE_IIS"), out var iisEnabled) && iisEnabled) + { + _logger.LogCritical("UNSUPPORTED HOSTING ENVIRONMENT Microsoft Internet Information Services. The option to run Jellyfin on IIS is an unsupported and untested feature. Only use at your own discretion."); + webHostBuilder.UseIIS(); + } + }) .ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(options, appPaths, startupConfig)) .UseSerilog() .Build(); |
