aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcvium <clausvium@gmail.com>2020-12-03 10:43:44 +0100
committercvium <clausvium@gmail.com>2020-12-03 10:43:44 +0100
commit7e0ea296c383b9b9cd778bb12834c2a73df3d1ea (patch)
treeb0acc0b4b3743f134f1073fcb17e2a3eadac181f
parentb57ace7888db78a655a00a277e7eb5c4a4eba294 (diff)
Move request validation to auth policies
-rw-r--r--Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs62
-rw-r--r--Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs58
-rw-r--r--Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs33
-rw-r--r--Jellyfin.Api/Constants/Policies.cs10
-rw-r--r--Jellyfin.Api/Controllers/SyncPlayController.cs3
-rw-r--r--Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs17
6 files changed, 120 insertions, 63 deletions
diff --git a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs
index 7e1f24f8c..0410048c4 100644
--- a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs
+++ b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs
@@ -102,11 +102,6 @@ namespace Emby.Server.Implementations.SyncPlay
/// <inheritdoc />
public void NewGroup(SessionInfo session, NewGroupRequest request, CancellationToken cancellationToken)
{
- if (!IsRequestValid(session, request))
- {
- return;
- }
-
// Locking required to access list of groups.
lock (_groupsLock)
{
@@ -132,11 +127,6 @@ namespace Emby.Server.Implementations.SyncPlay
/// <inheritdoc />
public void JoinGroup(SessionInfo session, JoinGroupRequest request, CancellationToken cancellationToken)
{
- if (!IsRequestValid(session, request))
- {
- return;
- }
-
var user = _userManager.GetUserById(session.UserId);
// Locking required to access list of groups.
@@ -190,11 +180,6 @@ namespace Emby.Server.Implementations.SyncPlay
/// <inheritdoc />
public void LeaveGroup(SessionInfo session, LeaveGroupRequest request, CancellationToken cancellationToken)
{
- if (!IsRequestValid(session, request))
- {
- return;
- }
-
// Locking required to access list of groups.
lock (_groupsLock)
{
@@ -230,11 +215,6 @@ namespace Emby.Server.Implementations.SyncPlay
/// <inheritdoc />
public List<GroupInfoDto> ListGroups(SessionInfo session, ListGroupsRequest request)
{
- if (!IsRequestValid(session, request))
- {
- return new List<GroupInfoDto>();
- }
-
var user = _userManager.GetUserById(session.UserId);
List<GroupInfoDto> list = new List<GroupInfoDto>();
@@ -260,11 +240,6 @@ namespace Emby.Server.Implementations.SyncPlay
/// <inheritdoc />
public void HandleRequest(SessionInfo session, IGroupPlaybackRequest request, CancellationToken cancellationToken)
{
- if (!IsRequestValid(session, request))
- {
- return;
- }
-
IGroupController group;
lock (_mapsLock)
{
@@ -417,42 +392,5 @@ namespace Emby.Server.Implementations.SyncPlay
throw new InvalidOperationException("Session was in wrong group!");
}
}
-
- /// <summary>
- /// Checks if a given session is allowed to make a given request.
- /// </summary>
- /// <param name="session">The session.</param>
- /// <param name="request">The request.</param>
- /// <returns><c>true</c> if the request is valid, <c>false</c> otherwise. Will return <c>false</c> also when session or request is null.</returns>
- private bool IsRequestValid(SessionInfo session, ISyncPlayRequest request)
- {
- if (session == null || (request == null))
- {
- return false;
- }
-
- var user = _userManager.GetUserById(session.UserId);
-
- if (user.SyncPlayAccess == SyncPlayAccess.None)
- {
- _logger.LogWarning("Session {SessionId} requested {RequestType} but does not have access to SyncPlay.", session.Id, request.Type);
-
- // TODO: rename to a more generic error. Next PR will fix this.
- var error = new GroupUpdate<string>(Guid.Empty, GroupUpdateType.JoinGroupDenied, string.Empty);
- _sessionManager.SendSyncPlayGroupUpdate(session, error, CancellationToken.None);
- return false;
- }
-
- if (request.Type.Equals(RequestType.NewGroup) && user.SyncPlayAccess != SyncPlayAccess.CreateAndJoinGroups)
- {
- _logger.LogWarning("Session {SessionId} does not have permission to create groups.", session.Id);
-
- var error = new GroupUpdate<string>(Guid.Empty, GroupUpdateType.CreateGroupDenied, string.Empty);
- _sessionManager.SendSyncPlayGroupUpdate(session, error, CancellationToken.None);
- return false;
- }
-
- return true;
- }
}
}
diff --git a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs
new file mode 100644
index 000000000..2c3294523
--- /dev/null
+++ b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs
@@ -0,0 +1,58 @@
+using System.Threading.Tasks;
+using Jellyfin.Api.Helpers;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Library;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+
+namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
+{
+ /// <summary>
+ /// Default authorization handler.
+ /// </summary>
+ public class SyncPlayAccessHandler : BaseAuthorizationHandler<SyncPlayAccessRequirement>
+ {
+ private readonly IUserManager _userManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SyncPlayAccessHandler"/> class.
+ /// </summary>
+ /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
+ /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
+ /// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
+ public SyncPlayAccessHandler(
+ IUserManager userManager,
+ INetworkManager networkManager,
+ IHttpContextAccessor httpContextAccessor)
+ : base(userManager, networkManager, httpContextAccessor)
+ {
+ _userManager = userManager;
+ }
+
+ /// <inheritdoc />
+ protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SyncPlayAccessRequirement requirement)
+ {
+ if (!ValidateClaims(context.User))
+ {
+ context.Fail();
+ return Task.CompletedTask;
+ }
+
+ var userId = ClaimHelpers.GetUserId(context.User);
+ var user = _userManager.GetUserById(userId!.Value);
+
+ if ((requirement.RequiredAccess.HasValue && user.SyncPlayAccess == requirement.RequiredAccess)
+ || (user.SyncPlayAccess == SyncPlayAccess.JoinGroups || user.SyncPlayAccess == SyncPlayAccess.CreateAndJoinGroups))
+ {
+ context.Succeed(requirement);
+ }
+ else
+ {
+ context.Fail();
+ }
+
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs
new file mode 100644
index 000000000..7fcaf69f6
--- /dev/null
+++ b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs
@@ -0,0 +1,33 @@
+using Jellyfin.Data.Enums;
+using Microsoft.AspNetCore.Authorization;
+
+namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
+{
+ /// <summary>
+ /// The default authorization requirement.
+ /// </summary>
+ public class SyncPlayAccessRequirement : IAuthorizationRequirement
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SyncPlayAccessRequirement"/> class.
+ /// </summary>
+ /// <param name="requiredAccess">A value of <see cref="SyncPlayAccess"/>.</param>
+ public SyncPlayAccessRequirement(SyncPlayAccess requiredAccess)
+ {
+ RequiredAccess = requiredAccess;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SyncPlayAccessRequirement"/> class.
+ /// </summary>
+ public SyncPlayAccessRequirement()
+ {
+ RequiredAccess = null;
+ }
+
+ /// <summary>
+ /// Gets the required SyncPlay access.
+ /// </summary>
+ public SyncPlayAccess? RequiredAccess { get; }
+ }
+}
diff --git a/Jellyfin.Api/Constants/Policies.cs b/Jellyfin.Api/Constants/Policies.cs
index 7d7767470..b35ceea1a 100644
--- a/Jellyfin.Api/Constants/Policies.cs
+++ b/Jellyfin.Api/Constants/Policies.cs
@@ -49,5 +49,15 @@ namespace Jellyfin.Api.Constants
/// Policy name for escaping schedule controls or requiring first time setup.
/// </summary>
public const string FirstTimeSetupOrIgnoreParentalControl = "FirstTimeSetupOrIgnoreParentalControl";
+
+ /// <summary>
+ /// Policy name for requiring access to SyncPlay.
+ /// </summary>
+ public const string SyncPlayAccess = "SyncPlayAccess";
+
+ /// <summary>
+ /// Policy name for requiring group creation access to SyncPlay.
+ /// </summary>
+ public const string SyncPlayCreateGroupAccess = "SyncPlayCreateGroupAccess";
}
}
diff --git a/Jellyfin.Api/Controllers/SyncPlayController.cs b/Jellyfin.Api/Controllers/SyncPlayController.cs
index ed5ea3c8a..763940c73 100644
--- a/Jellyfin.Api/Controllers/SyncPlayController.cs
+++ b/Jellyfin.Api/Controllers/SyncPlayController.cs
@@ -20,7 +20,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// The sync play controller.
/// </summary>
- [Authorize(Policy = Policies.DefaultAuthorization)]
+ [Authorize(Policy = Policies.SyncPlayAccess)]
public class SyncPlayController : BaseJellyfinApiController
{
private readonly ISessionManager _sessionManager;
@@ -51,6 +51,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("New")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayCreateGroupAccess)]
public ActionResult SyncPlayCreateGroup(
[FromBody, Required] NewGroupRequestBody requestData)
{
diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index 6cb88c9f7..cdcc4bb86 100644
--- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
@@ -15,9 +15,11 @@ using Jellyfin.Api.Auth.IgnoreParentalControlPolicy;
using Jellyfin.Api.Auth.LocalAccessOrRequiresElevationPolicy;
using Jellyfin.Api.Auth.LocalAccessPolicy;
using Jellyfin.Api.Auth.RequiresElevationPolicy;
+using Jellyfin.Api.Auth.SyncPlayAccessPolicy;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Controllers;
using Jellyfin.Api.ModelBinders;
+using Jellyfin.Data.Enums;
using Jellyfin.Server.Configuration;
using Jellyfin.Server.Filters;
using Jellyfin.Server.Formatters;
@@ -58,6 +60,7 @@ namespace Jellyfin.Server.Extensions
serviceCollection.AddSingleton<IAuthorizationHandler, LocalAccessHandler>();
serviceCollection.AddSingleton<IAuthorizationHandler, LocalAccessOrRequiresElevationHandler>();
serviceCollection.AddSingleton<IAuthorizationHandler, RequiresElevationHandler>();
+ serviceCollection.AddSingleton<IAuthorizationHandler, SyncPlayAccessHandler>();
return serviceCollection.AddAuthorizationCore(options =>
{
options.AddPolicy(
@@ -123,6 +126,20 @@ namespace Jellyfin.Server.Extensions
policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
policy.AddRequirements(new RequiresElevationRequirement());
});
+ options.AddPolicy(
+ Policies.SyncPlayAccess,
+ policy =>
+ {
+ policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
+ policy.AddRequirements(new SyncPlayAccessRequirement());
+ });
+ options.AddPolicy(
+ Policies.SyncPlayCreateGroupAccess,
+ policy =>
+ {
+ policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
+ policy.AddRequirements(new SyncPlayAccessRequirement(SyncPlayAccess.CreateAndJoinGroups));
+ });
});
}