using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
namespace Jellyfin.Api.Auth.DefaultAuthorizationPolicy
{
///
/// Default authorization handler.
///
public class DefaultAuthorizationHandler : AuthorizationHandler
{
private readonly IUserManager _userManager;
private readonly INetworkManager _networkManager;
private readonly IHttpContextAccessor _httpContextAccessor;
///
/// Initializes a new instance of the class.
///
/// Instance of the interface.
/// Instance of the interface.
/// Instance of the interface.
public DefaultAuthorizationHandler(
IUserManager userManager,
INetworkManager networkManager,
IHttpContextAccessor httpContextAccessor)
{
_userManager = userManager;
_networkManager = networkManager;
_httpContextAccessor = httpContextAccessor;
}
///
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DefaultAuthorizationRequirement requirement)
{
var isApiKey = context.User.GetIsApiKey();
var userId = context.User.GetUserId();
// This likely only happens during the wizard, so skip the default checks and let any other handlers do it
if (!isApiKey && userId.Equals(default))
{
return Task.CompletedTask;
}
if (isApiKey)
{
// Api keys are unrestricted.
context.Succeed(requirement);
return Task.CompletedTask;
}
var isInLocalNetwork = _httpContextAccessor.HttpContext is not null
&& _networkManager.IsInLocalNetwork(_httpContextAccessor.HttpContext.GetNormalizedRemoteIp());
var user = _userManager.GetUserById(userId);
if (user is null)
{
throw new ResourceNotFoundException();
}
// User cannot access remotely and user is remote
if (!isInLocalNetwork && !user.HasPermission(PermissionKind.EnableRemoteAccess))
{
context.Fail();
return Task.CompletedTask;
}
// Admins can do everything
if (context.User.IsInRole(UserRoles.Administrator))
{
context.Succeed(requirement);
return Task.CompletedTask;
}
// It's not great to have this check, but parental schedule must usually be honored except in a few rare cases
if (requirement.ValidateParentalSchedule && !user.IsParentalScheduleAllowed())
{
context.Fail();
return Task.CompletedTask;
}
// Only succeed if the requirement isn't a subclass as any subclassed requirement will handle success in its own handler
if (requirement.GetType() == typeof(DefaultAuthorizationRequirement))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
}