Update authorization policies for SyncPlay
This commit is contained in:
parent
a7b461adb4
commit
499f3ee950
|
@ -41,6 +41,12 @@ namespace Emby.Server.Implementations.SyncPlay
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The map between users and counter of active sessions.
|
||||||
|
/// </summary>
|
||||||
|
private readonly ConcurrentDictionary<Guid, int> _activeUsers =
|
||||||
|
new ConcurrentDictionary<Guid, int>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The map between sessions and groups.
|
/// The map between sessions and groups.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -122,6 +128,7 @@ namespace Emby.Server.Implementations.SyncPlay
|
||||||
throw new InvalidOperationException("Could not add session to group!");
|
throw new InvalidOperationException("Could not add session to group!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateSessionsCounter(session.UserId, 1);
|
||||||
group.CreateGroup(session, request, cancellationToken);
|
group.CreateGroup(session, request, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,6 +179,7 @@ namespace Emby.Server.Implementations.SyncPlay
|
||||||
if (existingGroup.GroupId.Equals(request.GroupId))
|
if (existingGroup.GroupId.Equals(request.GroupId))
|
||||||
{
|
{
|
||||||
// Restore session.
|
// Restore session.
|
||||||
|
UpdateSessionsCounter(session.UserId, 1);
|
||||||
group.SessionJoin(session, request, cancellationToken);
|
group.SessionJoin(session, request, cancellationToken);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -185,6 +193,7 @@ namespace Emby.Server.Implementations.SyncPlay
|
||||||
throw new InvalidOperationException("Could not add session to group!");
|
throw new InvalidOperationException("Could not add session to group!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateSessionsCounter(session.UserId, 1);
|
||||||
group.SessionJoin(session, request, cancellationToken);
|
group.SessionJoin(session, request, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,6 +232,7 @@ namespace Emby.Server.Implementations.SyncPlay
|
||||||
throw new InvalidOperationException("Could not remove session from group!");
|
throw new InvalidOperationException("Could not remove session from group!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateSessionsCounter(session.UserId, -1);
|
||||||
group.SessionLeave(session, request, cancellationToken);
|
group.SessionLeave(session, request, cancellationToken);
|
||||||
|
|
||||||
if (group.IsGroupEmpty())
|
if (group.IsGroupEmpty())
|
||||||
|
@ -318,6 +328,19 @@ namespace Emby.Server.Implementations.SyncPlay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsUserActive(Guid userId)
|
||||||
|
{
|
||||||
|
if (_activeUsers.TryGetValue(userId, out var sessionsCounter))
|
||||||
|
{
|
||||||
|
return sessionsCounter > 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases unmanaged and optionally managed resources.
|
/// Releases unmanaged and optionally managed resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -343,5 +366,26 @@ namespace Emby.Server.Implementations.SyncPlay
|
||||||
JoinGroup(session, request, CancellationToken.None);
|
JoinGroup(session, request, CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateSessionsCounter(Guid userId, int toAdd)
|
||||||
|
{
|
||||||
|
// Update sessions counter.
|
||||||
|
var newSessionsCounter = _activeUsers.AddOrUpdate(
|
||||||
|
userId,
|
||||||
|
1,
|
||||||
|
(key, sessionsCounter) => sessionsCounter + toAdd);
|
||||||
|
|
||||||
|
// Should never happen.
|
||||||
|
if (newSessionsCounter < 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Sessions counter is negative!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean record if user has no more active sessions.
|
||||||
|
if (newSessionsCounter == 0)
|
||||||
|
{
|
||||||
|
_activeUsers.TryRemove(new KeyValuePair<Guid, int>(userId, newSessionsCounter));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ using Jellyfin.Api.Helpers;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.SyncPlay;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
@ -13,20 +14,24 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SyncPlayAccessHandler : BaseAuthorizationHandler<SyncPlayAccessRequirement>
|
public class SyncPlayAccessHandler : BaseAuthorizationHandler<SyncPlayAccessRequirement>
|
||||||
{
|
{
|
||||||
|
private readonly ISyncPlayManager _syncPlayManager;
|
||||||
private readonly IUserManager _userManager;
|
private readonly IUserManager _userManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="SyncPlayAccessHandler"/> class.
|
/// Initializes a new instance of the <see cref="SyncPlayAccessHandler"/> class.
|
||||||
/// </summary>
|
/// </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="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
|
||||||
/// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
|
/// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
|
||||||
/// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
|
/// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
|
||||||
public SyncPlayAccessHandler(
|
public SyncPlayAccessHandler(
|
||||||
|
ISyncPlayManager syncPlayManager,
|
||||||
IUserManager userManager,
|
IUserManager userManager,
|
||||||
INetworkManager networkManager,
|
INetworkManager networkManager,
|
||||||
IHttpContextAccessor httpContextAccessor)
|
IHttpContextAccessor httpContextAccessor)
|
||||||
: base(userManager, networkManager, httpContextAccessor)
|
: base(userManager, networkManager, httpContextAccessor)
|
||||||
{
|
{
|
||||||
|
_syncPlayManager = syncPlayManager;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,10 +47,52 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
|
||||||
var userId = ClaimHelpers.GetUserId(context.User);
|
var userId = ClaimHelpers.GetUserId(context.User);
|
||||||
var user = _userManager.GetUserById(userId!.Value);
|
var user = _userManager.GetUserById(userId!.Value);
|
||||||
|
|
||||||
if ((requirement.RequiredAccess.HasValue && user.SyncPlayAccess == requirement.RequiredAccess)
|
if (requirement.RequiredAccess == SyncPlayAccessRequirementType.HasAccess)
|
||||||
|| user.SyncPlayAccess == SyncPlayAccess.CreateAndJoinGroups)
|
|
||||||
{
|
{
|
||||||
context.Succeed(requirement);
|
if (user.SyncPlayAccess == SyncPlayUserAccessType.CreateAndJoinGroups ||
|
||||||
|
user.SyncPlayAccess == SyncPlayUserAccessType.JoinGroups ||
|
||||||
|
_syncPlayManager.IsUserActive(userId!.Value))
|
||||||
|
{
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.CreateGroup)
|
||||||
|
{
|
||||||
|
if (user.SyncPlayAccess == SyncPlayUserAccessType.CreateAndJoinGroups)
|
||||||
|
{
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.JoinGroup)
|
||||||
|
{
|
||||||
|
if (user.SyncPlayAccess == SyncPlayUserAccessType.CreateAndJoinGroups ||
|
||||||
|
user.SyncPlayAccess == SyncPlayUserAccessType.JoinGroups)
|
||||||
|
{
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.IsInGroup)
|
||||||
|
{
|
||||||
|
if (_syncPlayManager.IsUserActive(userId!.Value))
|
||||||
|
{
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Fail();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,23 +11,15 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="SyncPlayAccessRequirement"/> class.
|
/// Initializes a new instance of the <see cref="SyncPlayAccessRequirement"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="requiredAccess">A value of <see cref="SyncPlayAccess"/>.</param>
|
/// <param name="requiredAccess">A value of <see cref="SyncPlayAccessRequirementType"/>.</param>
|
||||||
public SyncPlayAccessRequirement(SyncPlayAccess requiredAccess)
|
public SyncPlayAccessRequirement(SyncPlayAccessRequirementType requiredAccess)
|
||||||
{
|
{
|
||||||
RequiredAccess = requiredAccess;
|
RequiredAccess = requiredAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="SyncPlayAccessRequirement"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public SyncPlayAccessRequirement()
|
|
||||||
{
|
|
||||||
RequiredAccess = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the required SyncPlay access.
|
/// Gets the required SyncPlay access.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SyncPlayAccess? RequiredAccess { get; }
|
public SyncPlayAccessRequirementType RequiredAccess { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,13 +51,23 @@ namespace Jellyfin.Api.Constants
|
||||||
public const string FirstTimeSetupOrIgnoreParentalControl = "FirstTimeSetupOrIgnoreParentalControl";
|
public const string FirstTimeSetupOrIgnoreParentalControl = "FirstTimeSetupOrIgnoreParentalControl";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Policy name for requiring access to SyncPlay.
|
/// Policy name for accessing SyncPlay.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string SyncPlayAccess = "SyncPlayAccess";
|
public const string SyncPlayHasAccess = "SyncPlayHasAccess";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Policy name for requiring group creation access to SyncPlay.
|
/// Policy name for creating a SyncPlay group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string SyncPlayCreateGroupAccess = "SyncPlayCreateGroupAccess";
|
public const string SyncPlayCreateGroup = "SyncPlayCreateGroup";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Policy name for joining a SyncPlay group.
|
||||||
|
/// </summary>
|
||||||
|
public const string SyncPlayJoinGroup = "SyncPlayJoinGroup";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Policy name for accessing a SyncPlay group.
|
||||||
|
/// </summary>
|
||||||
|
public const string SyncPlayIsInGroup = "SyncPlayIsInGroup";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The sync play controller.
|
/// The sync play controller.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Authorize(Policy = Policies.SyncPlayAccess)]
|
[Authorize(Policy = Policies.SyncPlayHasAccess)]
|
||||||
public class SyncPlayController : BaseJellyfinApiController
|
public class SyncPlayController : BaseJellyfinApiController
|
||||||
{
|
{
|
||||||
private readonly ISessionManager _sessionManager;
|
private readonly ISessionManager _sessionManager;
|
||||||
|
@ -51,7 +51,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("New")]
|
[HttpPost("New")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[Authorize(Policy = Policies.SyncPlayCreateGroupAccess)]
|
[Authorize(Policy = Policies.SyncPlayCreateGroup)]
|
||||||
public ActionResult SyncPlayCreateGroup(
|
public ActionResult SyncPlayCreateGroup(
|
||||||
[FromBody, Required] NewGroupRequestDto requestData)
|
[FromBody, Required] NewGroupRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -69,7 +69,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("Join")]
|
[HttpPost("Join")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[Authorize(Policy = Policies.SyncPlayAccess)]
|
[Authorize(Policy = Policies.SyncPlayJoinGroup)]
|
||||||
public ActionResult SyncPlayJoinGroup(
|
public ActionResult SyncPlayJoinGroup(
|
||||||
[FromBody, Required] JoinGroupRequestDto requestData)
|
[FromBody, Required] JoinGroupRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -86,6 +86,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("Leave")]
|
[HttpPost("Leave")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlayLeaveGroup()
|
public ActionResult SyncPlayLeaveGroup()
|
||||||
{
|
{
|
||||||
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
|
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
|
||||||
|
@ -101,7 +102,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>An <see cref="IEnumerable{GroupInfoView}"/> containing the available SyncPlay groups.</returns>
|
/// <returns>An <see cref="IEnumerable{GroupInfoView}"/> containing the available SyncPlay groups.</returns>
|
||||||
[HttpGet("List")]
|
[HttpGet("List")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[Authorize(Policy = Policies.SyncPlayAccess)]
|
[Authorize(Policy = Policies.SyncPlayJoinGroup)]
|
||||||
public ActionResult<IEnumerable<GroupInfoDto>> SyncPlayGetGroups()
|
public ActionResult<IEnumerable<GroupInfoDto>> SyncPlayGetGroups()
|
||||||
{
|
{
|
||||||
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
|
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
|
||||||
|
@ -117,6 +118,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("SetNewQueue")]
|
[HttpPost("SetNewQueue")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlaySetNewQueue(
|
public ActionResult SyncPlaySetNewQueue(
|
||||||
[FromBody, Required] PlayRequestDto requestData)
|
[FromBody, Required] PlayRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -137,6 +139,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("SetPlaylistItem")]
|
[HttpPost("SetPlaylistItem")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlaySetPlaylistItem(
|
public ActionResult SyncPlaySetPlaylistItem(
|
||||||
[FromBody, Required] SetPlaylistItemRequestDto requestData)
|
[FromBody, Required] SetPlaylistItemRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -154,6 +157,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("RemoveFromPlaylist")]
|
[HttpPost("RemoveFromPlaylist")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlayRemoveFromPlaylist(
|
public ActionResult SyncPlayRemoveFromPlaylist(
|
||||||
[FromBody, Required] RemoveFromPlaylistRequestDto requestData)
|
[FromBody, Required] RemoveFromPlaylistRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -171,6 +175,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("MovePlaylistItem")]
|
[HttpPost("MovePlaylistItem")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlayMovePlaylistItem(
|
public ActionResult SyncPlayMovePlaylistItem(
|
||||||
[FromBody, Required] MovePlaylistItemRequestDto requestData)
|
[FromBody, Required] MovePlaylistItemRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -188,6 +193,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("Queue")]
|
[HttpPost("Queue")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlayQueue(
|
public ActionResult SyncPlayQueue(
|
||||||
[FromBody, Required] QueueRequestDto requestData)
|
[FromBody, Required] QueueRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -204,6 +210,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("Unpause")]
|
[HttpPost("Unpause")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlayUnpause()
|
public ActionResult SyncPlayUnpause()
|
||||||
{
|
{
|
||||||
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
|
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
|
||||||
|
@ -219,6 +226,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("Pause")]
|
[HttpPost("Pause")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlayPause()
|
public ActionResult SyncPlayPause()
|
||||||
{
|
{
|
||||||
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
|
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
|
||||||
|
@ -234,6 +242,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("Stop")]
|
[HttpPost("Stop")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlayStop()
|
public ActionResult SyncPlayStop()
|
||||||
{
|
{
|
||||||
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
|
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
|
||||||
|
@ -250,6 +259,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("Seek")]
|
[HttpPost("Seek")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlaySeek(
|
public ActionResult SyncPlaySeek(
|
||||||
[FromBody, Required] SeekRequestDto requestData)
|
[FromBody, Required] SeekRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -267,6 +277,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("Buffering")]
|
[HttpPost("Buffering")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlayBuffering(
|
public ActionResult SyncPlayBuffering(
|
||||||
[FromBody, Required] BufferRequestDto requestData)
|
[FromBody, Required] BufferRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -288,6 +299,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("Ready")]
|
[HttpPost("Ready")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlayReady(
|
public ActionResult SyncPlayReady(
|
||||||
[FromBody, Required] ReadyRequestDto requestData)
|
[FromBody, Required] ReadyRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -309,6 +321,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("SetIgnoreWait")]
|
[HttpPost("SetIgnoreWait")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlaySetIgnoreWait(
|
public ActionResult SyncPlaySetIgnoreWait(
|
||||||
[FromBody, Required] IgnoreWaitRequestDto requestData)
|
[FromBody, Required] IgnoreWaitRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -326,6 +339,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("NextItem")]
|
[HttpPost("NextItem")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlayNextItem(
|
public ActionResult SyncPlayNextItem(
|
||||||
[FromBody, Required] NextItemRequestDto requestData)
|
[FromBody, Required] NextItemRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -343,6 +357,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("PreviousItem")]
|
[HttpPost("PreviousItem")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlayPreviousItem(
|
public ActionResult SyncPlayPreviousItem(
|
||||||
[FromBody, Required] PreviousItemRequestDto requestData)
|
[FromBody, Required] PreviousItemRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -360,6 +375,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("SetRepeatMode")]
|
[HttpPost("SetRepeatMode")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlaySetRepeatMode(
|
public ActionResult SyncPlaySetRepeatMode(
|
||||||
[FromBody, Required] SetRepeatModeRequestDto requestData)
|
[FromBody, Required] SetRepeatModeRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
@ -377,6 +393,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
||||||
[HttpPost("SetShuffleMode")]
|
[HttpPost("SetShuffleMode")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[Authorize(Policy = Policies.SyncPlayIsInGroup)]
|
||||||
public ActionResult SyncPlaySetShuffleMode(
|
public ActionResult SyncPlaySetShuffleMode(
|
||||||
[FromBody, Required] SetShuffleModeRequestDto requestData)
|
[FromBody, Required] SetShuffleModeRequestDto requestData)
|
||||||
{
|
{
|
||||||
|
|
|
@ -71,7 +71,7 @@ namespace Jellyfin.Data.Entities
|
||||||
EnableAutoLogin = false;
|
EnableAutoLogin = false;
|
||||||
PlayDefaultAudioTrack = true;
|
PlayDefaultAudioTrack = true;
|
||||||
SubtitleMode = SubtitlePlaybackMode.Default;
|
SubtitleMode = SubtitlePlaybackMode.Default;
|
||||||
SyncPlayAccess = SyncPlayAccess.CreateAndJoinGroups;
|
SyncPlayAccess = SyncPlayUserAccessType.CreateAndJoinGroups;
|
||||||
|
|
||||||
AddDefaultPermissions();
|
AddDefaultPermissions();
|
||||||
AddDefaultPreferences();
|
AddDefaultPreferences();
|
||||||
|
@ -326,7 +326,7 @@ namespace Jellyfin.Data.Entities
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the level of sync play permissions this user has.
|
/// Gets or sets the level of sync play permissions this user has.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SyncPlayAccess SyncPlayAccess { get; set; }
|
public SyncPlayUserAccessType SyncPlayAccess { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the row version.
|
/// Gets or sets the row version.
|
||||||
|
|
28
Jellyfin.Data/Enums/SyncPlayAccessRequirementType.cs
Normal file
28
Jellyfin.Data/Enums/SyncPlayAccessRequirementType.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
namespace Jellyfin.Data.Enums
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Enum SyncPlayAccessRequirementType.
|
||||||
|
/// </summary>
|
||||||
|
public enum SyncPlayAccessRequirementType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// User must have access to SyncPlay, in some form.
|
||||||
|
/// </summary>
|
||||||
|
HasAccess = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// User must be able to create groups.
|
||||||
|
/// </summary>
|
||||||
|
CreateGroup = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// User must be able to join groups.
|
||||||
|
/// </summary>
|
||||||
|
JoinGroup = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// User must be in a group.
|
||||||
|
/// </summary>
|
||||||
|
IsInGroup = 3
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
namespace Jellyfin.Data.Enums
|
namespace Jellyfin.Data.Enums
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enum SyncPlayAccess.
|
/// Enum SyncPlayUserAccessType.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum SyncPlayAccess
|
public enum SyncPlayUserAccessType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User can create groups and join them.
|
/// User can create groups and join them.
|
|
@ -127,18 +127,32 @@ namespace Jellyfin.Server.Extensions
|
||||||
policy.AddRequirements(new RequiresElevationRequirement());
|
policy.AddRequirements(new RequiresElevationRequirement());
|
||||||
});
|
});
|
||||||
options.AddPolicy(
|
options.AddPolicy(
|
||||||
Policies.SyncPlayAccess,
|
Policies.SyncPlayHasAccess,
|
||||||
policy =>
|
policy =>
|
||||||
{
|
{
|
||||||
policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
|
policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
|
||||||
policy.AddRequirements(new SyncPlayAccessRequirement(SyncPlayAccess.JoinGroups));
|
policy.AddRequirements(new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.HasAccess));
|
||||||
});
|
});
|
||||||
options.AddPolicy(
|
options.AddPolicy(
|
||||||
Policies.SyncPlayCreateGroupAccess,
|
Policies.SyncPlayCreateGroup,
|
||||||
policy =>
|
policy =>
|
||||||
{
|
{
|
||||||
policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
|
policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
|
||||||
policy.AddRequirements(new SyncPlayAccessRequirement(SyncPlayAccess.CreateAndJoinGroups));
|
policy.AddRequirements(new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.CreateGroup));
|
||||||
|
});
|
||||||
|
options.AddPolicy(
|
||||||
|
Policies.SyncPlayJoinGroup,
|
||||||
|
policy =>
|
||||||
|
{
|
||||||
|
policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
|
||||||
|
policy.AddRequirements(new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.JoinGroup));
|
||||||
|
});
|
||||||
|
options.AddPolicy(
|
||||||
|
Policies.SyncPlayIsInGroup,
|
||||||
|
policy =>
|
||||||
|
{
|
||||||
|
policy.AddAuthenticationSchemes(AuthenticationSchemes.CustomAuthentication);
|
||||||
|
policy.AddRequirements(new SyncPlayAccessRequirement(SyncPlayAccessRequirementType.IsInGroup));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,5 +51,12 @@ namespace MediaBrowser.Controller.SyncPlay
|
||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
void HandleRequest(SessionInfo session, IGroupPlaybackRequest request, CancellationToken cancellationToken);
|
void HandleRequest(SessionInfo session, IGroupPlaybackRequest request, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether a user has an active session using SyncPlay.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">The user identifier to check.</param>
|
||||||
|
/// <returns><c>true</c> if the user is using SyncPlay; <c>false</c> otherwise.</returns>
|
||||||
|
bool IsUserActive(Guid userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ namespace MediaBrowser.Model.Users
|
||||||
/// Gets or sets a value indicating what SyncPlay features the user can access.
|
/// Gets or sets a value indicating what SyncPlay features the user can access.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>Access level to SyncPlay features.</value>
|
/// <value>Access level to SyncPlay features.</value>
|
||||||
public SyncPlayAccess SyncPlayAccess { get; set; }
|
public SyncPlayUserAccessType SyncPlayAccess { get; set; }
|
||||||
|
|
||||||
public UserPolicy()
|
public UserPolicy()
|
||||||
{
|
{
|
||||||
|
@ -160,7 +160,7 @@ namespace MediaBrowser.Model.Users
|
||||||
EnableContentDownloading = true;
|
EnableContentDownloading = true;
|
||||||
EnablePublicSharing = true;
|
EnablePublicSharing = true;
|
||||||
EnableRemoteAccess = true;
|
EnableRemoteAccess = true;
|
||||||
SyncPlayAccess = SyncPlayAccess.CreateAndJoinGroups;
|
SyncPlayAccess = SyncPlayUserAccessType.CreateAndJoinGroups;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user