diff options
| author | cvium <clausvium@gmail.com> | 2020-12-03 10:43:44 +0100 |
|---|---|---|
| committer | cvium <clausvium@gmail.com> | 2020-12-03 10:43:44 +0100 |
| commit | 7e0ea296c383b9b9cd778bb12834c2a73df3d1ea (patch) | |
| tree | b0acc0b4b3743f134f1073fcb17e2a3eadac181f | |
| parent | b57ace7888db78a655a00a277e7eb5c4a4eba294 (diff) | |
Move request validation to auth policies
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)); + }); }); } |
