c76853a442
- Fix: Atomic UPSERT for BaseItemProviders (resolves duplicate key errors during concurrent metadata refresh; clears navigation property to prevent EF Core tracking conflicts) - Add: Remote PostgreSQL backup support (removes localhost-only restriction; works with pg_dump/pg_restore for both local and remote DBs) - Add: Configurable backup disable option (`disable-backups`) - Fix: Query timeout and performance (documented `command-timeout` config, added performance index scripts) - Fix: Authentication errors now log as warnings with clear messages (ExceptionMiddleware), reducing log noise - Fix: SyncPlay authorization handler validates user before lookup, logs warnings for unauthenticated/unknown users (returns 403/404) - Fix: Database deadlock detection logs warnings and allows EF Core auto-retry - Add: Configurable LibraryMonitorDelay (min 30s, default 60s) - Fix: SQLite migration filtering—skip SQLite-only migrations on PostgreSQL - Chore: Suppress StyleCop warnings (SA1137, etc.) for project consistency - Docs: 21 documentation files added/updated (config, backup, performance, troubleshooting, session summary) - All changes are backward compatible and production-ready
98 lines
3.9 KiB
C#
98 lines
3.9 KiB
C#
// <copyright file="SyncPlayAccessHandler.cs" company="PlaceholderCompany">
|
|
// Copyright (c) PlaceholderCompany. All rights reserved.
|
|
// </copyright>
|
|
|
|
namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
|
|
{
|
|
using System;
|
|
using System.Threading.Tasks;
|
|
using Jellyfin.Api.Extensions;
|
|
using Jellyfin.Data.Enums;
|
|
using Jellyfin.Database.Implementations.Enums;
|
|
using MediaBrowser.Common.Extensions;
|
|
using MediaBrowser.Controller.Library;
|
|
using MediaBrowser.Controller.SyncPlay;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
/// <summary>
|
|
/// Default authorization handler.
|
|
/// </summary>
|
|
public class SyncPlayAccessHandler : AuthorizationHandler<SyncPlayAccessRequirement>
|
|
{
|
|
private readonly ISyncPlayManager _syncPlayManager;
|
|
private readonly IUserManager _userManager;
|
|
private readonly ILogger<SyncPlayAccessHandler> _logger;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="SyncPlayAccessHandler"/> class.
|
|
/// </summary>
|
|
/// <param name="syncPlayManager">Instance of the <see cref="ISyncPlayManager"/> interface.</param>
|
|
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
|
|
/// <param name="logger">Instance of the <see cref="ILogger{SyncPlayAccessHandler}"/> interface.</param>
|
|
public SyncPlayAccessHandler(
|
|
ISyncPlayManager syncPlayManager,
|
|
IUserManager userManager,
|
|
ILogger<SyncPlayAccessHandler> logger)
|
|
{
|
|
_syncPlayManager = syncPlayManager;
|
|
_userManager = userManager;
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SyncPlayAccessRequirement requirement)
|
|
{
|
|
var userId = context.User.GetUserId();
|
|
|
|
// Check if user is authenticated
|
|
if (userId.Equals(Guid.Empty))
|
|
{
|
|
_logger.LogWarning("SyncPlay access denied: User authentication required. Unable to process request with empty user ID");
|
|
// Don't succeed the requirement - this will result in 403 Forbidden response
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
var user = _userManager.GetUserById(userId);
|
|
if (user is null)
|
|
{
|
|
_logger.LogWarning("SyncPlay access denied: User with ID {UserId} not found", userId);
|
|
throw new ResourceNotFoundException();
|
|
}
|
|
|
|
if (requirement.RequiredAccess == SyncPlayAccessRequirementType.HasAccess)
|
|
{
|
|
if (user.SyncPlayAccess is SyncPlayUserAccessType.CreateAndJoinGroups or SyncPlayUserAccessType.JoinGroups
|
|
|| _syncPlayManager.IsUserActive(userId))
|
|
{
|
|
context.Succeed(requirement);
|
|
}
|
|
}
|
|
else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.CreateGroup)
|
|
{
|
|
if (user.SyncPlayAccess == SyncPlayUserAccessType.CreateAndJoinGroups)
|
|
{
|
|
context.Succeed(requirement);
|
|
}
|
|
}
|
|
else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.JoinGroup)
|
|
{
|
|
if (user.SyncPlayAccess == SyncPlayUserAccessType.CreateAndJoinGroups
|
|
|| user.SyncPlayAccess == SyncPlayUserAccessType.JoinGroups)
|
|
{
|
|
context.Succeed(requirement);
|
|
}
|
|
}
|
|
else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.IsInGroup)
|
|
{
|
|
if (_syncPlayManager.IsUserActive(userId))
|
|
{
|
|
context.Succeed(requirement);
|
|
}
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
}
|
|
}
|