mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-04 03:27:14 -05:00 
			
		
		
		
	Reworking authorization handling
This commit is contained in:
		
							parent
							
								
									765bd061b7
								
							
						
					
					
						commit
						71c18092e5
					
				@ -1,12 +1,13 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
using IdentityServer4.Extensions;
 | 
					using IdentityServer4.Extensions;
 | 
				
			||||||
 | 
					using IdentityServer4.Models;
 | 
				
			||||||
using IdentityServer4.Services;
 | 
					using IdentityServer4.Services;
 | 
				
			||||||
using Kyoo.Authentication.Models;
 | 
					using Kyoo.Authentication.Models;
 | 
				
			||||||
using Kyoo.Authentication.Views;
 | 
					using Kyoo.Authentication.Views;
 | 
				
			||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					 | 
				
			||||||
using Microsoft.AspNetCore.Builder;
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
using Microsoft.AspNetCore.Hosting;
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
@ -86,18 +87,20 @@ namespace Kyoo.Authentication
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			services.AddControllers();
 | 
								services.AddControllers();
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
			// TODO handle direct-videos with bearers (probably add a ?token query param and a app.Use to translate that for videos)
 | 
								// TODO handle direct-videos with bearers (probably add a cookie and a app.Use to translate that for videos)
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
			// TODO Check if tokens should be stored.
 | 
								// TODO Check if tokens should be stored.
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
			// TODO remove unused/commented code, add documentation.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			services.Configure<PermissionOption>(_configuration.GetSection(PermissionOption.Path));
 | 
								services.Configure<PermissionOption>(_configuration.GetSection(PermissionOption.Path));
 | 
				
			||||||
			services.Configure<CertificateOption>(_configuration.GetSection(CertificateOption.Path));
 | 
								services.Configure<CertificateOption>(_configuration.GetSection(CertificateOption.Path));
 | 
				
			||||||
			services.Configure<AuthenticationOption>(_configuration.GetSection(AuthenticationOption.Path));
 | 
								services.Configure<AuthenticationOption>(_configuration.GetSection(AuthenticationOption.Path));
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								List<Client> clients = new();
 | 
				
			||||||
 | 
								_configuration.GetSection("authentication:clients").Bind(clients);
 | 
				
			||||||
			CertificateOption certificateOptions = new();
 | 
								CertificateOption certificateOptions = new();
 | 
				
			||||||
			_configuration.GetSection(CertificateOption.Path).Bind(certificateOptions);
 | 
								_configuration.GetSection(CertificateOption.Path).Bind(certificateOptions);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
			services.AddIdentityServer(options =>
 | 
								services.AddIdentityServer(options =>
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					options.IssuerUri = publicUrl;
 | 
										options.IssuerUri = publicUrl;
 | 
				
			||||||
@ -108,11 +111,9 @@ namespace Kyoo.Authentication
 | 
				
			|||||||
				.AddInMemoryIdentityResources(IdentityContext.GetIdentityResources())
 | 
									.AddInMemoryIdentityResources(IdentityContext.GetIdentityResources())
 | 
				
			||||||
				.AddInMemoryApiScopes(IdentityContext.GetScopes())
 | 
									.AddInMemoryApiScopes(IdentityContext.GetScopes())
 | 
				
			||||||
				.AddInMemoryApiResources(IdentityContext.GetApis())
 | 
									.AddInMemoryApiResources(IdentityContext.GetApis())
 | 
				
			||||||
				.AddInMemoryClients(IdentityContext.GetClients())
 | 
									.AddInMemoryClients(IdentityContext.GetClients().Concat(clients))
 | 
				
			||||||
				.AddInMemoryClients(_configuration.GetSection("authentication:clients"))
 | 
					 | 
				
			||||||
				.AddProfileService<AccountApi>()
 | 
									.AddProfileService<AccountApi>()
 | 
				
			||||||
				.AddSigninKeys(certificateOptions);
 | 
									.AddSigninKeys(certificateOptions);
 | 
				
			||||||
			// TODO split scopes (kyoo.read should be task.read, video.read etc)
 | 
					 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
			services.AddAuthentication()
 | 
								services.AddAuthentication()
 | 
				
			||||||
				.AddJwtBearer(options =>
 | 
									.AddJwtBearer(options =>
 | 
				
			||||||
@ -121,25 +122,7 @@ namespace Kyoo.Authentication
 | 
				
			|||||||
					options.Audience = "kyoo";
 | 
										options.Audience = "kyoo";
 | 
				
			||||||
					options.RequireHttpsMetadata = false;
 | 
										options.RequireHttpsMetadata = false;
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
			
 | 
								services.AddSingleton<IPermissionValidator, PermissionValidatorFactory>();
 | 
				
			||||||
			services.AddAuthorization(options =>
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				AuthorizationPolicyBuilder scheme = new(JwtBearerDefaults.AuthenticationScheme);
 | 
					 | 
				
			||||||
				options.DefaultPolicy = scheme.RequireAuthenticatedUser().Build();
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
				string[] permissions = {"Read", "Write", "Play", "Admin"};
 | 
					 | 
				
			||||||
				foreach (string permission in permissions)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					options.AddPolicy(permission, policy =>
 | 
					 | 
				
			||||||
					{
 | 
					 | 
				
			||||||
						policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
 | 
					 | 
				
			||||||
						policy.AddRequirements(new AuthRequirement(permission));
 | 
					 | 
				
			||||||
						// Scopes are disables to support default permissions.
 | 
					 | 
				
			||||||
						// To enable them, use the following line: policy.RequireScope($"kyoo.{permission.ToLower()}");
 | 
					 | 
				
			||||||
					});
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
			services.AddSingleton<IAuthorizationHandler, AuthorizationValidatorHandler>();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			DefaultCorsPolicyService cors = new(_loggerFactory.CreateLogger<DefaultCorsPolicyService>())
 | 
								DefaultCorsPolicyService cors = new(_loggerFactory.CreateLogger<DefaultCorsPolicyService>())
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
 | 
				
			|||||||
@ -1,51 +0,0 @@
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					 | 
				
			||||||
using System.Security.Claims;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					 | 
				
			||||||
using IdentityServer4.Extensions;
 | 
					 | 
				
			||||||
using Kyoo.Authentication.Models;
 | 
					 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					 | 
				
			||||||
using Microsoft.Extensions.Options;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Kyoo.Authentication
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/// <summary>
 | 
					 | 
				
			||||||
	/// The default IAuthorizationHandler implementation.
 | 
					 | 
				
			||||||
	/// </summary>
 | 
					 | 
				
			||||||
	public class AuthorizationValidatorHandler : AuthorizationHandler<AuthRequirement>
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		/// <summary>
 | 
					 | 
				
			||||||
		/// The permissions options to retrieve default permissions.
 | 
					 | 
				
			||||||
		/// </summary>
 | 
					 | 
				
			||||||
		private readonly IOptionsMonitor<PermissionOption> _options;
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		/// <summary>
 | 
					 | 
				
			||||||
		/// Create a new <see cref="AuthorizationValidatorHandler"/>.
 | 
					 | 
				
			||||||
		/// </summary>
 | 
					 | 
				
			||||||
		/// <param name="options">The option containing default values.</param>
 | 
					 | 
				
			||||||
		public AuthorizationValidatorHandler(IOptionsMonitor<PermissionOption> options)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			_options = options;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/// <inheritdoc />
 | 
					 | 
				
			||||||
		protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthRequirement requirement)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if (context.User.IsAuthenticated())
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				Claim perms = context.User.Claims.FirstOrDefault(x => x.Type == "permissions");
 | 
					 | 
				
			||||||
				if (perms != null && perms.Value.Split(",").Contains(requirement.Permission.ToLower()))
 | 
					 | 
				
			||||||
					context.Succeed(requirement);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				ICollection<string> defaultPerms = _options.CurrentValue.Default;
 | 
					 | 
				
			||||||
				if (defaultPerms?.Contains(requirement.Permission.ToLower()) == true)
 | 
					 | 
				
			||||||
					context.Succeed(requirement);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return Task.CompletedTask;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										126
									
								
								Kyoo.Authentication/Controllers/PremissionValidator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								Kyoo.Authentication/Controllers/PremissionValidator.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,126 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Kyoo.Authentication.Models;
 | 
				
			||||||
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Authentication;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Authentication.JwtBearer;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc.Filters;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Kyoo.Authentication
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/// <summary>
 | 
				
			||||||
 | 
						/// A permission validator to validate permission with user Permission array
 | 
				
			||||||
 | 
						/// or the default array from the configurations if the user is not logged. 
 | 
				
			||||||
 | 
						/// </summary>
 | 
				
			||||||
 | 
						public class PermissionValidatorFactory : IPermissionValidator
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The permissions options to retrieve default permissions.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							private readonly IOptionsMonitor<PermissionOption> _options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Create a new factory with the given options
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <param name="options">The option containing default values.</param>
 | 
				
			||||||
 | 
							public PermissionValidatorFactory(IOptionsMonitor<PermissionOption> options)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								_options = options;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <inheritdoc />
 | 
				
			||||||
 | 
							public IFilterMetadata Create(PermissionAttribute attribute)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return new PermissionValidator(attribute.AsPermissionString(), _options);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <inheritdoc />
 | 
				
			||||||
 | 
							public IFilterMetadata Create(PartialPermissionAttribute attribute)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return new PermissionValidator((object)attribute.Type ?? attribute.Kind, _options);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The authorization filter used by <see cref="PermissionValidatorFactory"/>
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							private class PermissionValidator : IAsyncAuthorizationFilter
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								/// <summary>
 | 
				
			||||||
 | 
								/// The permission to validate
 | 
				
			||||||
 | 
								/// </summary>
 | 
				
			||||||
 | 
								private readonly string _permission;
 | 
				
			||||||
 | 
								/// <summary>
 | 
				
			||||||
 | 
								/// Information about partial items.
 | 
				
			||||||
 | 
								/// </summary>
 | 
				
			||||||
 | 
								private readonly object _partialInfo;
 | 
				
			||||||
 | 
								/// <summary>
 | 
				
			||||||
 | 
								/// The permissions options to retrieve default permissions.
 | 
				
			||||||
 | 
								/// </summary>
 | 
				
			||||||
 | 
								private readonly IOptionsMonitor<PermissionOption> _options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/// <summary>
 | 
				
			||||||
 | 
								/// Create a new permission validator with the given options
 | 
				
			||||||
 | 
								/// </summary>
 | 
				
			||||||
 | 
								/// <param name="permission">The permission to validate</param>
 | 
				
			||||||
 | 
								/// <param name="options">The option containing default values.</param>
 | 
				
			||||||
 | 
								public PermissionValidator(string permission, IOptionsMonitor<PermissionOption> options)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									_permission = permission;
 | 
				
			||||||
 | 
									_options = options;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/// <summary>
 | 
				
			||||||
 | 
								/// Create a new permission validator with the given options
 | 
				
			||||||
 | 
								/// </summary>
 | 
				
			||||||
 | 
								/// <param name="partialInfo">The partial permission to validate</param>
 | 
				
			||||||
 | 
								/// <param name="options">The option containing default values.</param>
 | 
				
			||||||
 | 
								public PermissionValidator(object partialInfo, IOptionsMonitor<PermissionOption> options)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									_partialInfo = partialInfo;
 | 
				
			||||||
 | 
									_options = options;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/// <inheritdoc />
 | 
				
			||||||
 | 
								public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									string permission = _permission;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (_partialInfo != null)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										switch (context.HttpContext.Items["PermissionType"])
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											case string perm when _partialInfo is Kind kind:
 | 
				
			||||||
 | 
												 permission = $"{perm}.{kind.ToString().ToLower()}";
 | 
				
			||||||
 | 
												break;
 | 
				
			||||||
 | 
											case Kind kind when _partialInfo is string partial:
 | 
				
			||||||
 | 
												permission = $"{partial}.{kind.ToString().ToLower()}";
 | 
				
			||||||
 | 
												break;
 | 
				
			||||||
 | 
											case null:
 | 
				
			||||||
 | 
												context.HttpContext.Items["PermissionType"] = _partialInfo;
 | 
				
			||||||
 | 
												return;
 | 
				
			||||||
 | 
											default:
 | 
				
			||||||
 | 
												throw new ArgumentException("Multiple non-matching partial permission attribute " +
 | 
				
			||||||
 | 
												                            "are not supported.");
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									AuthenticateResult res = await context.HttpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);
 | 
				
			||||||
 | 
									if (res.Succeeded)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										if (res.Principal.GetPermissions().All(x => x != permission))
 | 
				
			||||||
 | 
											context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										if (res.Failure != null || _options.CurrentValue.Default.All(x => x != permission))
 | 
				
			||||||
 | 
											context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
using System.Security.Claims;
 | 
					using System.Security.Claims;
 | 
				
			||||||
using IdentityModel;
 | 
					using IdentityModel;
 | 
				
			||||||
using IdentityServer4;
 | 
					using IdentityServer4;
 | 
				
			||||||
@ -35,8 +36,19 @@ namespace Kyoo.Authentication
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			return new(user.ID.ToString())
 | 
								return new(user.ID.ToString())
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				DisplayName = user.Username
 | 
									DisplayName = user.Username,
 | 
				
			||||||
 | 
									AdditionalClaims = new[] {new Claim("permissions", string.Join(',', user.Permissions))}
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Get the permissions of an user.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <param name="user">The user</param>
 | 
				
			||||||
 | 
							/// <returns>The list of permissions</returns>
 | 
				
			||||||
 | 
							public static ICollection<string> GetPermissions(this ClaimsPrincipal user)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return user.Claims.FirstOrDefault(x => x.Type == "permissions")?.Value.Split(',');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,24 +0,0 @@
 | 
				
			|||||||
using Microsoft.AspNetCore.Authorization;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Kyoo.Authentication
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/// <summary>
 | 
					 | 
				
			||||||
	/// The requirement of Kyoo's authentication policies.
 | 
					 | 
				
			||||||
	/// </summary>
 | 
					 | 
				
			||||||
	public class AuthRequirement : IAuthorizationRequirement
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		/// <summary>
 | 
					 | 
				
			||||||
		/// The name of the permission
 | 
					 | 
				
			||||||
		/// </summary>
 | 
					 | 
				
			||||||
		public string Permission { get; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/// <summary>
 | 
					 | 
				
			||||||
		/// Create a new <see cref="AuthRequirement"/> for the given permission.
 | 
					 | 
				
			||||||
		/// </summary>
 | 
					 | 
				
			||||||
		/// <param name="permission">The permission needed</param>
 | 
					 | 
				
			||||||
		public AuthRequirement(string permission)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			Permission = permission;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -13,8 +13,8 @@ using Kyoo.Authentication.Models.DTO;
 | 
				
			|||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models;
 | 
					using Kyoo.Models;
 | 
				
			||||||
using Kyoo.Models.Exceptions;
 | 
					using Kyoo.Models.Exceptions;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					 | 
				
			||||||
using Microsoft.AspNetCore.Authentication;
 | 
					using Microsoft.AspNetCore.Authentication;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Authorization;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using Microsoft.Extensions.Options;
 | 
					using Microsoft.Extensions.Options;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,23 +1,149 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc.Filters;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Kyoo.Models.Attributes
 | 
					namespace Kyoo.Models.Permissions
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						/// <summary>
 | 
				
			||||||
 | 
						/// The kind of permission needed.
 | 
				
			||||||
 | 
						/// </summary>
 | 
				
			||||||
 | 
						public enum Kind
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Read,
 | 
				
			||||||
 | 
							Write,
 | 
				
			||||||
 | 
							Create,
 | 
				
			||||||
 | 
							Delete
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	/// <summary>
 | 
						/// <summary>
 | 
				
			||||||
	/// Specify permissions needed for the API.
 | 
						/// Specify permissions needed for the API.
 | 
				
			||||||
	/// </summary>
 | 
						/// </summary>
 | 
				
			||||||
	[AttributeUsage(AttributeTargets.Method)]
 | 
						[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
 | 
				
			||||||
	public class PermissionAttribute : Attribute
 | 
						public class PermissionAttribute : Attribute, IFilterFactory
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		public enum Kind
 | 
							/// <summary>
 | 
				
			||||||
		{
 | 
							/// The needed permission as string.
 | 
				
			||||||
			Read,
 | 
							/// </summary>
 | 
				
			||||||
			Write,
 | 
							private readonly string _permission;
 | 
				
			||||||
			Admin
 | 
					
 | 
				
			||||||
		}
 | 
							/// <summary>
 | 
				
			||||||
		
 | 
							/// Ask a permission to run an action. 
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <param name="type">
 | 
				
			||||||
 | 
							/// The type of the action
 | 
				
			||||||
 | 
							/// (if the type ends with api, it will be removed. This allow you to use nameof(YourApi)).
 | 
				
			||||||
 | 
							/// </param>
 | 
				
			||||||
 | 
							/// <param name="permission">The kind of permission needed</param>
 | 
				
			||||||
		public PermissionAttribute(string type, Kind permission)
 | 
							public PermissionAttribute(string type, Kind permission)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			
 | 
								if (type.EndsWith("API", StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
 | 
									type = type[..^3];
 | 
				
			||||||
 | 
								_permission = $"{type.ToLower()}.{permission.ToString().ToLower()}";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <inheritdoc />
 | 
				
			||||||
 | 
							public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return serviceProvider.GetRequiredService<IPermissionValidator>().Create(this);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <inheritdoc />
 | 
				
			||||||
 | 
							public bool IsReusable => true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Return this permission attribute as a string
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <returns>The string representation.</returns>
 | 
				
			||||||
 | 
							public string AsPermissionString()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return _permission;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// <summary>
 | 
				
			||||||
 | 
						/// Specify one part of a permissions needed for the API (the kind or the type).
 | 
				
			||||||
 | 
						/// </summary>
 | 
				
			||||||
 | 
						[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
 | 
				
			||||||
 | 
						public class PartialPermissionAttribute : Attribute, IFilterFactory
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The needed permission type.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public string Type { get; }
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The needed permission kind.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public Kind Kind { get; }
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Ask a permission to run an action. 
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <remarks>
 | 
				
			||||||
 | 
							/// With this attribute, you can only specify a type or a kind.
 | 
				
			||||||
 | 
							/// To have a valid permission attribute, you must specify the kind and the permission using two attributes.
 | 
				
			||||||
 | 
							/// Those attributes can be dispatched at different places (one on the class, one on the method for example).
 | 
				
			||||||
 | 
							/// If you don't put exactly two of those attributes, the permission attribute will be ill-formed and will
 | 
				
			||||||
 | 
							/// lead to unspecified behaviors. 
 | 
				
			||||||
 | 
							/// </remarks>
 | 
				
			||||||
 | 
							/// <param name="type">
 | 
				
			||||||
 | 
							/// The type of the action
 | 
				
			||||||
 | 
							/// (if the type ends with api, it will be removed. This allow you to use nameof(YourApi)).
 | 
				
			||||||
 | 
							/// </param>
 | 
				
			||||||
 | 
							public PartialPermissionAttribute(string type)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (type.EndsWith("API", StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
 | 
									type = type[..^3];
 | 
				
			||||||
 | 
								Type = type.ToLower();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Ask a permission to run an action. 
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <remarks>
 | 
				
			||||||
 | 
							/// With this attribute, you can only specify a type or a kind.
 | 
				
			||||||
 | 
							/// To have a valid permission attribute, you must specify the kind and the permission using two attributes.
 | 
				
			||||||
 | 
							/// Those attributes can be dispatched at different places (one on the class, one on the method for example).
 | 
				
			||||||
 | 
							/// If you don't put exactly two of those attributes, the permission attribute will be ill-formed and will
 | 
				
			||||||
 | 
							/// lead to unspecified behaviors. 
 | 
				
			||||||
 | 
							/// </remarks>
 | 
				
			||||||
 | 
							/// <param name="permission">The kind of permission needed</param>
 | 
				
			||||||
 | 
							public PartialPermissionAttribute(Kind permission)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Kind = permission;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <inheritdoc />
 | 
				
			||||||
 | 
							public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return serviceProvider.GetRequiredService<IPermissionValidator>().Create(this);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <inheritdoc />
 | 
				
			||||||
 | 
							public bool IsReusable => true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// <summary>
 | 
				
			||||||
 | 
						/// A service to validate permissions
 | 
				
			||||||
 | 
						/// </summary>
 | 
				
			||||||
 | 
						public interface IPermissionValidator
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Create an IAuthorizationFilter that will be used to validate permissions.
 | 
				
			||||||
 | 
							/// This can registered with any lifetime.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <param name="attribute">The permission attribute to validate</param>
 | 
				
			||||||
 | 
							/// <returns>An authorization filter used to validate the permission</returns>
 | 
				
			||||||
 | 
							IFilterMetadata Create(PermissionAttribute attribute);
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Create an IAuthorizationFilter that will be used to validate permissions.
 | 
				
			||||||
 | 
							/// This can registered with any lifetime.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <param name="attribute">
 | 
				
			||||||
 | 
							/// A partial attribute to validate. See <see cref="PartialPermissionAttribute"/>.
 | 
				
			||||||
 | 
							/// </param>
 | 
				
			||||||
 | 
							/// <returns>An authorization filter used to validate the permission</returns>
 | 
				
			||||||
 | 
							IFilterMetadata Create(PartialPermissionAttribute attribute);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -5,7 +5,7 @@ using System.Threading.Tasks;
 | 
				
			|||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models;
 | 
					using Kyoo.Models;
 | 
				
			||||||
using Kyoo.Models.Exceptions;
 | 
					using Kyoo.Models.Exceptions;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -26,7 +26,7 @@ namespace Kyoo.CommonApi
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{id:int}")]
 | 
							[HttpGet("{id:int}")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public virtual async Task<ActionResult<T>> Get(int id)
 | 
							public virtual async Task<ActionResult<T>> Get(int id)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			T ret = await _repository.GetOrDefault(id);
 | 
								T ret = await _repository.GetOrDefault(id);
 | 
				
			||||||
@ -36,7 +36,7 @@ namespace Kyoo.CommonApi
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{slug}")]
 | 
							[HttpGet("{slug}")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public virtual async Task<ActionResult<T>> Get(string slug)
 | 
							public virtual async Task<ActionResult<T>> Get(string slug)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			T ret = await _repository.Get(slug);
 | 
								T ret = await _repository.Get(slug);
 | 
				
			||||||
@ -46,7 +46,7 @@ namespace Kyoo.CommonApi
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpGet("count")]
 | 
							[HttpGet("count")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public virtual async Task<ActionResult<int>> GetCount([FromQuery] Dictionary<string, string> where)
 | 
							public virtual async Task<ActionResult<int>> GetCount([FromQuery] Dictionary<string, string> where)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -60,7 +60,7 @@ namespace Kyoo.CommonApi
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet]
 | 
							[HttpGet]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public virtual async Task<ActionResult<Page<T>>> GetAll([FromQuery] string sortBy,
 | 
							public virtual async Task<ActionResult<Page<T>>> GetAll([FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
			[FromQuery] Dictionary<string, string> where,
 | 
								[FromQuery] Dictionary<string, string> where,
 | 
				
			||||||
@ -90,7 +90,7 @@ namespace Kyoo.CommonApi
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpPost]
 | 
							[HttpPost]
 | 
				
			||||||
		[Authorize(Policy = "Write")]
 | 
							[PartialPermission(Kind.Create)]
 | 
				
			||||||
		public virtual async Task<ActionResult<T>> Create([FromBody] T resource)
 | 
							public virtual async Task<ActionResult<T>> Create([FromBody] T resource)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -109,7 +109,7 @@ namespace Kyoo.CommonApi
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpPut]
 | 
							[HttpPut]
 | 
				
			||||||
		[Authorize(Policy = "Write")]
 | 
							[PartialPermission(Kind.Write)]
 | 
				
			||||||
		public virtual async Task<ActionResult<T>> Edit([FromQuery] bool resetOld, [FromBody] T resource)
 | 
							public virtual async Task<ActionResult<T>> Edit([FromQuery] bool resetOld, [FromBody] T resource)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -128,7 +128,7 @@ namespace Kyoo.CommonApi
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpPut("{id:int}")]
 | 
							[HttpPut("{id:int}")]
 | 
				
			||||||
		[Authorize(Policy = "Write")]
 | 
							[PartialPermission(Kind.Write)]
 | 
				
			||||||
		public virtual async Task<ActionResult<T>> Edit(int id, [FromQuery] bool resetOld, [FromBody] T resource)
 | 
							public virtual async Task<ActionResult<T>> Edit(int id, [FromQuery] bool resetOld, [FromBody] T resource)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			resource.ID = id;
 | 
								resource.ID = id;
 | 
				
			||||||
@ -143,7 +143,7 @@ namespace Kyoo.CommonApi
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpPut("{slug}")]
 | 
							[HttpPut("{slug}")]
 | 
				
			||||||
		[Authorize(Policy = "Write")]
 | 
							[PartialPermission(Kind.Write)]
 | 
				
			||||||
		public virtual async Task<ActionResult<T>> Edit(string slug, [FromQuery] bool resetOld, [FromBody] T resource)
 | 
							public virtual async Task<ActionResult<T>> Edit(string slug, [FromQuery] bool resetOld, [FromBody] T resource)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -159,7 +159,7 @@ namespace Kyoo.CommonApi
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpDelete("{id:int}")]
 | 
							[HttpDelete("{id:int}")]
 | 
				
			||||||
		[Authorize(Policy = "Write")]
 | 
							[PartialPermission(Kind.Delete)]
 | 
				
			||||||
		public virtual async Task<IActionResult> Delete(int id)
 | 
							public virtual async Task<IActionResult> Delete(int id)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -175,7 +175,7 @@ namespace Kyoo.CommonApi
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpDelete("{slug}")]
 | 
							[HttpDelete("{slug}")]
 | 
				
			||||||
		[Authorize(Policy = "Write")]
 | 
							[PartialPermission(Kind.Delete)]
 | 
				
			||||||
		public virtual async Task<IActionResult> Delete(string slug)
 | 
							public virtual async Task<IActionResult> Delete(string slug)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -190,7 +190,7 @@ namespace Kyoo.CommonApi
 | 
				
			|||||||
			return Ok();
 | 
								return Ok();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[Authorize(Policy = "Write")]
 | 
							[PartialPermission(Kind.Delete)]
 | 
				
			||||||
		public virtual async Task<IActionResult> Delete(Dictionary<string, string> where)
 | 
							public virtual async Task<IActionResult> Delete(Dictionary<string, string> where)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,7 @@ using Kyoo.Postgresql;
 | 
				
			|||||||
using Kyoo.Tasks;
 | 
					using Kyoo.Tasks;
 | 
				
			||||||
using Microsoft.AspNetCore.Builder;
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
using Microsoft.AspNetCore.Hosting;
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using Microsoft.AspNetCore.SpaServices.AngularCli;
 | 
					using Microsoft.AspNetCore.SpaServices.AngularCli;
 | 
				
			||||||
using Microsoft.AspNetCore.StaticFiles;
 | 
					using Microsoft.AspNetCore.StaticFiles;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
@ -131,7 +132,7 @@ namespace Kyoo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			app.UseEndpoints(endpoints =>
 | 
								app.UseEndpoints(endpoints =>
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				endpoints.MapControllerRoute("Kyoo", "api/{controller=Home}/{action=Index}/{id?}");
 | 
									endpoints.MapControllers();
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ using Kyoo.Models;
 | 
				
			|||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Kyoo.CommonApi;
 | 
					using Kyoo.CommonApi;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Kyoo.Api
 | 
					namespace Kyoo.Api
 | 
				
			||||||
@ -14,6 +14,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
	[Route("api/collection")]
 | 
						[Route("api/collection")]
 | 
				
			||||||
	[Route("api/collections")]
 | 
						[Route("api/collections")]
 | 
				
			||||||
	[ApiController]
 | 
						[ApiController]
 | 
				
			||||||
 | 
						[PartialPermission(nameof(CollectionApi))]
 | 
				
			||||||
	public class CollectionApi : CrudApi<Collection>
 | 
						public class CollectionApi : CrudApi<Collection>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private readonly ILibraryManager _libraryManager;
 | 
							private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
@ -26,7 +27,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{id:int}/show")]
 | 
							[HttpGet("{id:int}/show")]
 | 
				
			||||||
		[HttpGet("{id:int}/shows")]
 | 
							[HttpGet("{id:int}/shows")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Show>>> GetShows(int id,
 | 
							public async Task<ActionResult<Page<Show>>> GetShows(int id,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -52,7 +53,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{slug}/show")]
 | 
							[HttpGet("{slug}/show")]
 | 
				
			||||||
		[HttpGet("{slug}/shows")]
 | 
							[HttpGet("{slug}/shows")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Show>>> GetShows(string slug,
 | 
							public async Task<ActionResult<Page<Show>>> GetShows(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -66,7 +67,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
					new Sort<Show>(sortBy),
 | 
										new Sort<Show>(sortBy),
 | 
				
			||||||
					new Pagination(limit, afterID));
 | 
										new Pagination(limit, afterID));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (!resources.Any() && await _libraryManager.Get<Collection>(slug) == null)
 | 
									if (!resources.Any() && await _libraryManager.GetOrDefault<Collection>(slug) == null)
 | 
				
			||||||
					return NotFound();
 | 
										return NotFound();
 | 
				
			||||||
				return Page(resources, limit);
 | 
									return Page(resources, limit);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -78,7 +79,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{id:int}/library")]
 | 
							[HttpGet("{id:int}/library")]
 | 
				
			||||||
		[HttpGet("{id:int}/libraries")]
 | 
							[HttpGet("{id:int}/libraries")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Library>>> GetLibraries(int id,
 | 
							public async Task<ActionResult<Page<Library>>> GetLibraries(int id,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -104,7 +105,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{slug}/library")]
 | 
							[HttpGet("{slug}/library")]
 | 
				
			||||||
		[HttpGet("{slug}/libraries")]
 | 
							[HttpGet("{slug}/libraries")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Library>>> GetLibraries(string slug,
 | 
							public async Task<ActionResult<Page<Library>>> GetLibraries(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -118,7 +119,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
					new Sort<Library>(sortBy),
 | 
										new Sort<Library>(sortBy),
 | 
				
			||||||
					new Pagination(limit, afterID));
 | 
										new Pagination(limit, afterID));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (!resources.Any() && await _libraryManager.Get<Collection>(slug) == null)
 | 
									if (!resources.Any() && await _libraryManager.GetOrDefault<Collection>(slug) == null)
 | 
				
			||||||
					return NotFound();
 | 
										return NotFound();
 | 
				
			||||||
				return Page(resources, limit);
 | 
									return Page(resources, limit);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ using System.Threading.Tasks;
 | 
				
			|||||||
using Kyoo.CommonApi;
 | 
					using Kyoo.CommonApi;
 | 
				
			||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models.Exceptions;
 | 
					using Kyoo.Models.Exceptions;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Kyoo.Api
 | 
					namespace Kyoo.Api
 | 
				
			||||||
@ -15,6 +15,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
	[Route("api/episode")]
 | 
						[Route("api/episode")]
 | 
				
			||||||
	[Route("api/episodes")]
 | 
						[Route("api/episodes")]
 | 
				
			||||||
	[ApiController]
 | 
						[ApiController]
 | 
				
			||||||
 | 
						[PartialPermission(nameof(EpisodeApi))]
 | 
				
			||||||
	public class EpisodeApi : CrudApi<Episode>
 | 
						public class EpisodeApi : CrudApi<Episode>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private readonly ILibraryManager _libraryManager;
 | 
							private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
@ -33,21 +34,27 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{episodeID:int}/show")]
 | 
							[HttpGet("{episodeID:int}/show")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Show>> GetShow(int episodeID)
 | 
							public async Task<ActionResult<Show>> GetShow(int episodeID)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return await _libraryManager.Get<Show>(x => x.Episodes.Any(y => y.ID  == episodeID));
 | 
								Show ret =  await _libraryManager.GetOrDefault<Show>(x => x.Episodes.Any(y => y.ID  == episodeID));
 | 
				
			||||||
 | 
								if (ret == null)
 | 
				
			||||||
 | 
									return NotFound();
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/show")]
 | 
							[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/show")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Show>> GetShow(string showSlug, int seasonNumber, int episodeNumber)
 | 
							public async Task<ActionResult<Show>> GetShow(string showSlug, int seasonNumber, int episodeNumber)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return await _libraryManager.Get<Show>(showSlug);
 | 
								Show ret = await _libraryManager.GetOrDefault<Show>(showSlug);
 | 
				
			||||||
 | 
								if (ret == null)
 | 
				
			||||||
 | 
									return NotFound();
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/show")]
 | 
							[HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/show")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Show>> GetShow(int showID, int seasonNumber, int episodeNumber)
 | 
							public async Task<ActionResult<Show>> GetShow(int showID, int seasonNumber, int episodeNumber)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Show ret = await _libraryManager.GetOrDefault<Show>(showID);
 | 
								Show ret = await _libraryManager.GetOrDefault<Show>(showID);
 | 
				
			||||||
@ -57,7 +64,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{episodeID:int}/season")]
 | 
							[HttpGet("{episodeID:int}/season")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Season>> GetSeason(int episodeID)
 | 
							public async Task<ActionResult<Season>> GetSeason(int episodeID)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Season ret = await _libraryManager.GetOrDefault<Season>(x => x.Episodes.Any(y => y.ID == episodeID));
 | 
								Season ret = await _libraryManager.GetOrDefault<Season>(x => x.Episodes.Any(y => y.ID == episodeID));
 | 
				
			||||||
@ -67,7 +74,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/season")]
 | 
							[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/season")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Season>> GetSeason(string showSlug, int seasonNumber, int episodeNumber)
 | 
							public async Task<ActionResult<Season>> GetSeason(string showSlug, int seasonNumber, int episodeNumber)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -81,7 +88,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/season")]
 | 
							[HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/season")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Season>> GetSeason(int showID, int seasonNumber, int episodeNumber)
 | 
							public async Task<ActionResult<Season>> GetSeason(int showID, int seasonNumber, int episodeNumber)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -96,7 +103,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{episodeID:int}/track")]
 | 
							[HttpGet("{episodeID:int}/track")]
 | 
				
			||||||
		[HttpGet("{episodeID:int}/tracks")]
 | 
							[HttpGet("{episodeID:int}/tracks")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Track>>> GetEpisode(int episodeID,
 | 
							public async Task<ActionResult<Page<Track>>> GetEpisode(int episodeID,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -122,7 +129,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showID:int}-s{seasonNumber:int}e{episodeNumber:int}/track")]
 | 
							[HttpGet("{showID:int}-s{seasonNumber:int}e{episodeNumber:int}/track")]
 | 
				
			||||||
		[HttpGet("{showID:int}-s{seasonNumber:int}e{episodeNumber:int}/tracks")]
 | 
							[HttpGet("{showID:int}-s{seasonNumber:int}e{episodeNumber:int}/tracks")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Track>>> GetEpisode(int showID,
 | 
							public async Task<ActionResult<Page<Track>>> GetEpisode(int showID,
 | 
				
			||||||
			int seasonNumber,
 | 
								int seasonNumber,
 | 
				
			||||||
			int episodeNumber,
 | 
								int episodeNumber,
 | 
				
			||||||
@ -152,7 +159,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{slug}-s{seasonNumber:int}e{episodeNumber:int}/track")]
 | 
							[HttpGet("{slug}-s{seasonNumber:int}e{episodeNumber:int}/track")]
 | 
				
			||||||
		[HttpGet("{slug}-s{seasonNumber:int}e{episodeNumber:int}/tracks")]
 | 
							[HttpGet("{slug}-s{seasonNumber:int}e{episodeNumber:int}/tracks")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Track>>> GetEpisode(string slug,
 | 
							public async Task<ActionResult<Page<Track>>> GetEpisode(string slug,
 | 
				
			||||||
			int seasonNumber,
 | 
								int seasonNumber,
 | 
				
			||||||
			int episodeNumber,
 | 
								int episodeNumber,
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ using System.Threading.Tasks;
 | 
				
			|||||||
using Kyoo.CommonApi;
 | 
					using Kyoo.CommonApi;
 | 
				
			||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models;
 | 
					using Kyoo.Models;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -14,6 +14,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
	[Route("api/genre")]
 | 
						[Route("api/genre")]
 | 
				
			||||||
	[Route("api/genres")]
 | 
						[Route("api/genres")]
 | 
				
			||||||
	[ApiController]
 | 
						[ApiController]
 | 
				
			||||||
 | 
						[PartialPermission(nameof(GenreApi))]
 | 
				
			||||||
	public class GenreApi : CrudApi<Genre>
 | 
						public class GenreApi : CrudApi<Genre>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private readonly ILibraryManager _libraryManager;
 | 
							private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
@ -26,7 +27,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{id:int}/show")]
 | 
							[HttpGet("{id:int}/show")]
 | 
				
			||||||
		[HttpGet("{id:int}/shows")]
 | 
							[HttpGet("{id:int}/shows")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Show>>> GetShows(int id,
 | 
							public async Task<ActionResult<Page<Show>>> GetShows(int id,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -52,7 +53,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{slug}/show")]
 | 
							[HttpGet("{slug}/show")]
 | 
				
			||||||
		[HttpGet("{slug}/shows")]
 | 
							[HttpGet("{slug}/shows")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Show>>> GetShows(string slug,
 | 
							public async Task<ActionResult<Page<Show>>> GetShows(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -66,7 +67,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
					new Sort<Show>(sortBy),
 | 
										new Sort<Show>(sortBy),
 | 
				
			||||||
					new Pagination(limit, afterID));
 | 
										new Pagination(limit, afterID));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (!resources.Any() && await _libraryManager.Get<Genre>(slug) == null)
 | 
									if (!resources.Any() && await _libraryManager.GetOrDefault<Genre>(slug) == null)
 | 
				
			||||||
					return NotFound();
 | 
										return NotFound();
 | 
				
			||||||
				return Page(resources, limit);
 | 
									return Page(resources, limit);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ using Kyoo.Models;
 | 
				
			|||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Kyoo.CommonApi;
 | 
					using Kyoo.CommonApi;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Kyoo.Api
 | 
					namespace Kyoo.Api
 | 
				
			||||||
@ -14,6 +14,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
	[Route("api/library")]
 | 
						[Route("api/library")]
 | 
				
			||||||
	[Route("api/libraries")]
 | 
						[Route("api/libraries")]
 | 
				
			||||||
	[ApiController]
 | 
						[ApiController]
 | 
				
			||||||
 | 
						[PartialPermission(nameof(LibraryAPI))]
 | 
				
			||||||
	public class LibraryAPI : CrudApi<Library>
 | 
						public class LibraryAPI : CrudApi<Library>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private readonly ILibraryManager _libraryManager;
 | 
							private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
@ -26,7 +27,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
			_taskManager = taskManager;
 | 
								_taskManager = taskManager;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[Authorize(Policy = "Write")]
 | 
							[PartialPermission(Kind.Create)]
 | 
				
			||||||
		public override async Task<ActionResult<Library>> Create(Library resource)
 | 
							public override async Task<ActionResult<Library>> Create(Library resource)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			ActionResult<Library> result = await base.Create(resource);
 | 
								ActionResult<Library> result = await base.Create(resource);
 | 
				
			||||||
@ -37,7 +38,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{id:int}/show")]
 | 
							[HttpGet("{id:int}/show")]
 | 
				
			||||||
		[HttpGet("{id:int}/shows")]
 | 
							[HttpGet("{id:int}/shows")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Show>>> GetShows(int id,
 | 
							public async Task<ActionResult<Page<Show>>> GetShows(int id,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -63,7 +64,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{slug}/show")]
 | 
							[HttpGet("{slug}/show")]
 | 
				
			||||||
		[HttpGet("{slug}/shows")]
 | 
							[HttpGet("{slug}/shows")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Show>>> GetShows(string slug,
 | 
							public async Task<ActionResult<Page<Show>>> GetShows(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -77,7 +78,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
					new Sort<Show>(sortBy),
 | 
										new Sort<Show>(sortBy),
 | 
				
			||||||
					new Pagination(limit, afterID));
 | 
										new Pagination(limit, afterID));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (!resources.Any() && await _libraryManager.Get<Library>(slug) == null)
 | 
									if (!resources.Any() && await _libraryManager.GetOrDefault<Library>(slug) == null)
 | 
				
			||||||
					return NotFound();
 | 
										return NotFound();
 | 
				
			||||||
				return Page(resources, limit);
 | 
									return Page(resources, limit);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -89,7 +90,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{id:int}/collection")]
 | 
							[HttpGet("{id:int}/collection")]
 | 
				
			||||||
		[HttpGet("{id:int}/collections")]
 | 
							[HttpGet("{id:int}/collections")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Collection>>> GetCollections(int id,
 | 
							public async Task<ActionResult<Page<Collection>>> GetCollections(int id,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -115,7 +116,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{slug}/collection")]
 | 
							[HttpGet("{slug}/collection")]
 | 
				
			||||||
		[HttpGet("{slug}/collections")]
 | 
							[HttpGet("{slug}/collections")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Collection>>> GetCollections(string slug,
 | 
							public async Task<ActionResult<Page<Collection>>> GetCollections(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -129,7 +130,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
					new Sort<Collection>(sortBy),
 | 
										new Sort<Collection>(sortBy),
 | 
				
			||||||
					new Pagination(limit, afterID));
 | 
										new Pagination(limit, afterID));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (!resources.Any() && await _libraryManager.Get<Library>(slug) == null)
 | 
									if (!resources.Any() && await _libraryManager.GetOrDefault<Library>(slug) == null)
 | 
				
			||||||
					return NotFound();
 | 
										return NotFound();
 | 
				
			||||||
				return Page(resources, limit);
 | 
									return Page(resources, limit);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -141,7 +142,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{id:int}/item")]
 | 
							[HttpGet("{id:int}/item")]
 | 
				
			||||||
		[HttpGet("{id:int}/items")]
 | 
							[HttpGet("{id:int}/items")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<LibraryItem>>> GetItems(int id,
 | 
							public async Task<ActionResult<Page<LibraryItem>>> GetItems(int id,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -167,7 +168,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{slug}/item")]
 | 
							[HttpGet("{slug}/item")]
 | 
				
			||||||
		[HttpGet("{slug}/items")]
 | 
							[HttpGet("{slug}/items")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<LibraryItem>>> GetItems(string slug,
 | 
							public async Task<ActionResult<Page<LibraryItem>>> GetItems(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -181,7 +182,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
					new Sort<LibraryItem>(sortBy),
 | 
										new Sort<LibraryItem>(sortBy),
 | 
				
			||||||
					new Pagination(limit, afterID));
 | 
										new Pagination(limit, afterID));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (!resources.Any() && await _libraryManager.Get<Library>(slug) == null)
 | 
									if (!resources.Any() && await _libraryManager.GetOrDefault<Library>(slug) == null)
 | 
				
			||||||
					return NotFound();
 | 
										return NotFound();
 | 
				
			||||||
				return Page(resources, limit);
 | 
									return Page(resources, limit);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ using Kyoo.CommonApi;
 | 
				
			|||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models;
 | 
					using Kyoo.Models;
 | 
				
			||||||
using Kyoo.Models.Exceptions;
 | 
					using Kyoo.Models.Exceptions;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -29,7 +29,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpGet]
 | 
							[HttpGet]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[Permission(nameof(LibraryItemApi), Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<LibraryItem>>> GetAll([FromQuery] string sortBy,
 | 
							public async Task<ActionResult<Page<LibraryItem>>> GetAll([FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
			[FromQuery] Dictionary<string, string> where,
 | 
								[FromQuery] Dictionary<string, string> where,
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ using Kyoo.CommonApi;
 | 
				
			|||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models;
 | 
					using Kyoo.Models;
 | 
				
			||||||
using Kyoo.Models.Exceptions;
 | 
					using Kyoo.Models.Exceptions;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -13,6 +13,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	[Route("api/people")]
 | 
						[Route("api/people")]
 | 
				
			||||||
	[ApiController]
 | 
						[ApiController]
 | 
				
			||||||
 | 
						[PartialPermission(nameof(PeopleApi))]
 | 
				
			||||||
	public class PeopleApi : CrudApi<People>
 | 
						public class PeopleApi : CrudApi<People>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private readonly ILibraryManager _libraryManager;
 | 
							private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
@ -32,7 +33,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{id:int}/role")]
 | 
							[HttpGet("{id:int}/role")]
 | 
				
			||||||
		[HttpGet("{id:int}/roles")]
 | 
							[HttpGet("{id:int}/roles")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<PeopleRole>>> GetRoles(int id,
 | 
							public async Task<ActionResult<Page<PeopleRole>>> GetRoles(int id,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -60,7 +61,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{slug}/role")]
 | 
							[HttpGet("{slug}/role")]
 | 
				
			||||||
		[HttpGet("{slug}/roles")]
 | 
							[HttpGet("{slug}/roles")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<PeopleRole>>> GetRoles(string slug,
 | 
							public async Task<ActionResult<Page<PeopleRole>>> GetRoles(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ using System.Threading.Tasks;
 | 
				
			|||||||
using Kyoo.CommonApi;
 | 
					using Kyoo.CommonApi;
 | 
				
			||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models;
 | 
					using Kyoo.Models;
 | 
				
			||||||
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -10,13 +11,14 @@ namespace Kyoo.Api
 | 
				
			|||||||
	[Route("api/provider")]
 | 
						[Route("api/provider")]
 | 
				
			||||||
	[Route("api/providers")]
 | 
						[Route("api/providers")]
 | 
				
			||||||
	[ApiController]
 | 
						[ApiController]
 | 
				
			||||||
	public class ProviderAPI : CrudApi<Provider>
 | 
						[PartialPermission(nameof(ProviderApi))]
 | 
				
			||||||
 | 
						public class ProviderApi : CrudApi<Provider>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private readonly IThumbnailsManager _thumbnails;
 | 
							private readonly IThumbnailsManager _thumbnails;
 | 
				
			||||||
		private readonly ILibraryManager _libraryManager;
 | 
							private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
		private readonly IFileManager _files;
 | 
							private readonly IFileManager _files;
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		public ProviderAPI(ILibraryManager libraryManager,
 | 
							public ProviderApi(ILibraryManager libraryManager,
 | 
				
			||||||
			IConfiguration config,
 | 
								IConfiguration config,
 | 
				
			||||||
			IFileManager files,
 | 
								IFileManager files,
 | 
				
			||||||
			IThumbnailsManager thumbnails)
 | 
								IThumbnailsManager thumbnails)
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models;
 | 
					using Kyoo.Models;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Kyoo.Api
 | 
					namespace Kyoo.Api
 | 
				
			||||||
@ -19,7 +19,12 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpGet]
 | 
							[HttpGet]
 | 
				
			||||||
		[Authorize(Policy="Read")]
 | 
							[Permission(nameof(Collection), Kind.Read)]
 | 
				
			||||||
 | 
							[Permission(nameof(Show), Kind.Read)]
 | 
				
			||||||
 | 
							[Permission(nameof(Episode), Kind.Read)]
 | 
				
			||||||
 | 
							[Permission(nameof(People), Kind.Read)]
 | 
				
			||||||
 | 
							[Permission(nameof(Genre), Kind.Read)]
 | 
				
			||||||
 | 
							[Permission(nameof(Studio), Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<SearchResult>> Search(string query)
 | 
							public async Task<ActionResult<SearchResult>> Search(string query)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return new SearchResult
 | 
								return new SearchResult
 | 
				
			||||||
@ -36,7 +41,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("collection")]
 | 
							[HttpGet("collection")]
 | 
				
			||||||
		[HttpGet("collections")]
 | 
							[HttpGet("collections")]
 | 
				
			||||||
		[Authorize(Policy="Read")]
 | 
							[Permission(nameof(Collection), Kind.Read)]
 | 
				
			||||||
		public Task<ICollection<Collection>> SearchCollections(string query)
 | 
							public Task<ICollection<Collection>> SearchCollections(string query)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return _libraryManager.Search<Collection>(query);
 | 
								return _libraryManager.Search<Collection>(query);
 | 
				
			||||||
@ -44,7 +49,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("show")]
 | 
							[HttpGet("show")]
 | 
				
			||||||
		[HttpGet("shows")]
 | 
							[HttpGet("shows")]
 | 
				
			||||||
		[Authorize(Policy="Read")]
 | 
							[Permission(nameof(Show), Kind.Read)]
 | 
				
			||||||
		public Task<ICollection<Show>> SearchShows(string query)
 | 
							public Task<ICollection<Show>> SearchShows(string query)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return _libraryManager.Search<Show>(query);
 | 
								return _libraryManager.Search<Show>(query);
 | 
				
			||||||
@ -52,14 +57,14 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("episode")]
 | 
							[HttpGet("episode")]
 | 
				
			||||||
		[HttpGet("episodes")]
 | 
							[HttpGet("episodes")]
 | 
				
			||||||
		[Authorize(Policy="Read")]
 | 
							[Permission(nameof(Episode), Kind.Read)]
 | 
				
			||||||
		public Task<ICollection<Episode>> SearchEpisodes(string query)
 | 
							public Task<ICollection<Episode>> SearchEpisodes(string query)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return _libraryManager.Search<Episode>(query);
 | 
								return _libraryManager.Search<Episode>(query);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("people")]
 | 
							[HttpGet("people")]
 | 
				
			||||||
		[Authorize(Policy="Read")]
 | 
							[Permission(nameof(People), Kind.Read)]
 | 
				
			||||||
		public Task<ICollection<People>> SearchPeople(string query)
 | 
							public Task<ICollection<People>> SearchPeople(string query)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return _libraryManager.Search<People>(query);
 | 
								return _libraryManager.Search<People>(query);
 | 
				
			||||||
@ -67,7 +72,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("genre")]
 | 
							[HttpGet("genre")]
 | 
				
			||||||
		[HttpGet("genres")]
 | 
							[HttpGet("genres")]
 | 
				
			||||||
		[Authorize(Policy="Read")]
 | 
							[Permission(nameof(Genre), Kind.Read)]
 | 
				
			||||||
		public Task<ICollection<Genre>> SearchGenres(string query)
 | 
							public Task<ICollection<Genre>> SearchGenres(string query)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return _libraryManager.Search<Genre>(query);
 | 
								return _libraryManager.Search<Genre>(query);
 | 
				
			||||||
@ -75,7 +80,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("studio")]
 | 
							[HttpGet("studio")]
 | 
				
			||||||
		[HttpGet("studios")]
 | 
							[HttpGet("studios")]
 | 
				
			||||||
		[Authorize(Policy="Read")]
 | 
							[Permission(nameof(Studio), Kind.Read)]
 | 
				
			||||||
		public Task<ICollection<Studio>> SearchStudios(string query)
 | 
							public Task<ICollection<Studio>> SearchStudios(string query)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return _libraryManager.Search<Studio>(query);
 | 
								return _libraryManager.Search<Studio>(query);
 | 
				
			||||||
 | 
				
			|||||||
@ -4,9 +4,9 @@ using System.Threading.Tasks;
 | 
				
			|||||||
using Kyoo.CommonApi;
 | 
					using Kyoo.CommonApi;
 | 
				
			||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models;
 | 
					using Kyoo.Models;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Kyoo.Api
 | 
					namespace Kyoo.Api
 | 
				
			||||||
@ -14,6 +14,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
	[Route("api/season")]
 | 
						[Route("api/season")]
 | 
				
			||||||
	[Route("api/seasons")]
 | 
						[Route("api/seasons")]
 | 
				
			||||||
	[ApiController]
 | 
						[ApiController]
 | 
				
			||||||
 | 
						[PartialPermission(nameof(SeasonApi))]
 | 
				
			||||||
	public class SeasonApi : CrudApi<Season>
 | 
						public class SeasonApi : CrudApi<Season>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private readonly ILibraryManager _libraryManager;
 | 
							private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
@ -33,7 +34,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{seasonID:int}/episode")]
 | 
							[HttpGet("{seasonID:int}/episode")]
 | 
				
			||||||
		[HttpGet("{seasonID:int}/episodes")]
 | 
							[HttpGet("{seasonID:int}/episodes")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Episode>>> GetEpisode(int seasonID,
 | 
							public async Task<ActionResult<Page<Episode>>> GetEpisode(int seasonID,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -59,7 +60,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showSlug}-s{seasonNumber:int}/episode")]
 | 
							[HttpGet("{showSlug}-s{seasonNumber:int}/episode")]
 | 
				
			||||||
		[HttpGet("{showSlug}-s{seasonNumber:int}/episodes")]
 | 
							[HttpGet("{showSlug}-s{seasonNumber:int}/episodes")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Episode>>> GetEpisode(string showSlug,
 | 
							public async Task<ActionResult<Page<Episode>>> GetEpisode(string showSlug,
 | 
				
			||||||
			int seasonNumber,
 | 
								int seasonNumber,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
@ -87,7 +88,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showID:int}-s{seasonNumber:int}/episode")]
 | 
							[HttpGet("{showID:int}-s{seasonNumber:int}/episode")]
 | 
				
			||||||
		[HttpGet("{showID:int}-s{seasonNumber:int}/episodes")]
 | 
							[HttpGet("{showID:int}-s{seasonNumber:int}/episodes")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Episode>>> GetEpisode(int showID,
 | 
							public async Task<ActionResult<Page<Episode>>> GetEpisode(int showID,
 | 
				
			||||||
			int seasonNumber,
 | 
								int seasonNumber,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
@ -113,21 +114,27 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{seasonID:int}/show")]
 | 
							[HttpGet("{seasonID:int}/show")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Show>> GetShow(int seasonID)
 | 
							public async Task<ActionResult<Show>> GetShow(int seasonID)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return await _libraryManager.Get<Show>(x => x.Seasons.Any(y => y.ID == seasonID));
 | 
								Show ret = await _libraryManager.GetOrDefault<Show>(x => x.Seasons.Any(y => y.ID == seasonID));
 | 
				
			||||||
 | 
								if (ret == null)
 | 
				
			||||||
 | 
									return NotFound();
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showSlug}-s{seasonNumber:int}/show")]
 | 
							[HttpGet("{showSlug}-s{seasonNumber:int}/show")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Show>> GetShow(string showSlug, int seasonNumber)
 | 
							public async Task<ActionResult<Show>> GetShow(string showSlug, int seasonNumber)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return await _libraryManager.Get<Show>(showSlug);
 | 
								Show ret = await _libraryManager.GetOrDefault<Show>(showSlug);
 | 
				
			||||||
 | 
								if (ret == null)
 | 
				
			||||||
 | 
									return NotFound();
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showID:int}-s{seasonNumber:int}/show")]
 | 
							[HttpGet("{showID:int}-s{seasonNumber:int}/show")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Show>> GetShow(int showID, int seasonNumber)
 | 
							public async Task<ActionResult<Show>> GetShow(int showID, int seasonNumber)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Show ret = await _libraryManager.GetOrDefault<Show>(showID);
 | 
								Show ret = await _libraryManager.GetOrDefault<Show>(showID);
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ using System.Threading.Tasks;
 | 
				
			|||||||
using Kyoo.CommonApi;
 | 
					using Kyoo.CommonApi;
 | 
				
			||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models.Exceptions;
 | 
					using Kyoo.Models.Exceptions;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Kyoo.Api
 | 
					namespace Kyoo.Api
 | 
				
			||||||
@ -16,6 +16,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
	[Route("api/show")]
 | 
						[Route("api/show")]
 | 
				
			||||||
	[Route("api/shows")]
 | 
						[Route("api/shows")]
 | 
				
			||||||
	[ApiController]
 | 
						[ApiController]
 | 
				
			||||||
 | 
						[PartialPermission(nameof(ShowApi))]
 | 
				
			||||||
	public class ShowApi : CrudApi<Show>
 | 
						public class ShowApi : CrudApi<Show>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private readonly ILibraryManager _libraryManager;
 | 
							private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
@ -35,7 +36,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{showID:int}/season")]
 | 
							[HttpGet("{showID:int}/season")]
 | 
				
			||||||
		[HttpGet("{showID:int}/seasons")]
 | 
							[HttpGet("{showID:int}/seasons")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Season>>> GetSeasons(int showID,
 | 
							public async Task<ActionResult<Page<Season>>> GetSeasons(int showID,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -61,7 +62,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{slug}/season")]
 | 
							[HttpGet("{slug}/season")]
 | 
				
			||||||
		[HttpGet("{slug}/seasons")]
 | 
							[HttpGet("{slug}/seasons")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Season>>> GetSeasons(string slug,
 | 
							public async Task<ActionResult<Page<Season>>> GetSeasons(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -87,7 +88,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showID:int}/episode")]
 | 
							[HttpGet("{showID:int}/episode")]
 | 
				
			||||||
		[HttpGet("{showID:int}/episodes")]
 | 
							[HttpGet("{showID:int}/episodes")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Episode>>> GetEpisodes(int showID,
 | 
							public async Task<ActionResult<Page<Episode>>> GetEpisodes(int showID,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -113,7 +114,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{slug}/episode")]
 | 
							[HttpGet("{slug}/episode")]
 | 
				
			||||||
		[HttpGet("{slug}/episodes")]
 | 
							[HttpGet("{slug}/episodes")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Episode>>> GetEpisodes(string slug,
 | 
							public async Task<ActionResult<Page<Episode>>> GetEpisodes(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -138,7 +139,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showID:int}/people")]
 | 
							[HttpGet("{showID:int}/people")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<PeopleRole>>> GetPeople(int showID,
 | 
							public async Task<ActionResult<Page<PeopleRole>>> GetPeople(int showID,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -163,7 +164,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{slug}/people")]
 | 
							[HttpGet("{slug}/people")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<PeopleRole>>> GetPeople(string slug,
 | 
							public async Task<ActionResult<Page<PeopleRole>>> GetPeople(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -189,7 +190,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showID:int}/genre")]
 | 
							[HttpGet("{showID:int}/genre")]
 | 
				
			||||||
		[HttpGet("{showID:int}/genres")]
 | 
							[HttpGet("{showID:int}/genres")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Genre>>> GetGenres(int showID,
 | 
							public async Task<ActionResult<Page<Genre>>> GetGenres(int showID,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -215,7 +216,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{slug}/genre")]
 | 
							[HttpGet("{slug}/genre")]
 | 
				
			||||||
		[HttpGet("{slug}/genres")]
 | 
							[HttpGet("{slug}/genres")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Genre>>> GetGenre(string slug,
 | 
							public async Task<ActionResult<Page<Genre>>> GetGenre(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -240,7 +241,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showID:int}/studio")]
 | 
							[HttpGet("{showID:int}/studio")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Studio>> GetStudio(int showID)
 | 
							public async Task<ActionResult<Studio>> GetStudio(int showID)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -254,7 +255,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{slug}/studio")]
 | 
							[HttpGet("{slug}/studio")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Studio>> GetStudio(string slug)
 | 
							public async Task<ActionResult<Studio>> GetStudio(string slug)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -269,7 +270,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showID:int}/library")]
 | 
							[HttpGet("{showID:int}/library")]
 | 
				
			||||||
		[HttpGet("{showID:int}/libraries")]
 | 
							[HttpGet("{showID:int}/libraries")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Library>>> GetLibraries(int showID,
 | 
							public async Task<ActionResult<Page<Library>>> GetLibraries(int showID,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -295,7 +296,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{slug}/library")]
 | 
							[HttpGet("{slug}/library")]
 | 
				
			||||||
		[HttpGet("{slug}/libraries")]
 | 
							[HttpGet("{slug}/libraries")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Library>>> GetLibraries(string slug,
 | 
							public async Task<ActionResult<Page<Library>>> GetLibraries(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -321,7 +322,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showID:int}/collection")]
 | 
							[HttpGet("{showID:int}/collection")]
 | 
				
			||||||
		[HttpGet("{showID:int}/collections")]
 | 
							[HttpGet("{showID:int}/collections")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Collection>>> GetCollections(int showID,
 | 
							public async Task<ActionResult<Page<Collection>>> GetCollections(int showID,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -347,7 +348,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{slug}/collection")]
 | 
							[HttpGet("{slug}/collection")]
 | 
				
			||||||
		[HttpGet("{slug}/collections")]
 | 
							[HttpGet("{slug}/collections")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Collection>>> GetCollections(string slug,
 | 
							public async Task<ActionResult<Page<Collection>>> GetCollections(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -373,7 +374,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{slug}/font")]
 | 
							[HttpGet("{slug}/font")]
 | 
				
			||||||
		[HttpGet("{slug}/fonts")]
 | 
							[HttpGet("{slug}/fonts")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Dictionary<string, string>>> GetFonts(string slug)
 | 
							public async Task<ActionResult<Dictionary<string, string>>> GetFonts(string slug)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -392,7 +393,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{showSlug}/font/{slug}")]
 | 
							[HttpGet("{showSlug}/font/{slug}")]
 | 
				
			||||||
		[HttpGet("{showSlug}/fonts/{slug}")]
 | 
							[HttpGet("{showSlug}/fonts/{slug}")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<IActionResult> GetFont(string showSlug, string slug)
 | 
							public async Task<IActionResult> GetFont(string showSlug, string slug)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ using System.Threading.Tasks;
 | 
				
			|||||||
using Kyoo.CommonApi;
 | 
					using Kyoo.CommonApi;
 | 
				
			||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models;
 | 
					using Kyoo.Models;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -14,6 +14,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
	[Route("api/studio")]
 | 
						[Route("api/studio")]
 | 
				
			||||||
	[Route("api/studios")]
 | 
						[Route("api/studios")]
 | 
				
			||||||
	[ApiController]
 | 
						[ApiController]
 | 
				
			||||||
 | 
						[PartialPermission(nameof(ShowApi))]
 | 
				
			||||||
	public class StudioAPI : CrudApi<Studio>
 | 
						public class StudioAPI : CrudApi<Studio>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private readonly ILibraryManager _libraryManager;
 | 
							private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
@ -26,7 +27,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{id:int}/show")]
 | 
							[HttpGet("{id:int}/show")]
 | 
				
			||||||
		[HttpGet("{id:int}/shows")]
 | 
							[HttpGet("{id:int}/shows")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Show>>> GetShows(int id,
 | 
							public async Task<ActionResult<Page<Show>>> GetShows(int id,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
@ -52,7 +53,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{slug}/show")]
 | 
							[HttpGet("{slug}/show")]
 | 
				
			||||||
		[HttpGet("{slug}/shows")]
 | 
							[HttpGet("{slug}/shows")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Page<Show>>> GetShows(string slug,
 | 
							public async Task<ActionResult<Page<Show>>> GetShows(string slug,
 | 
				
			||||||
			[FromQuery] string sortBy,
 | 
								[FromQuery] string sortBy,
 | 
				
			||||||
			[FromQuery] int afterID,
 | 
								[FromQuery] int afterID,
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ using System.Collections.Generic;
 | 
				
			|||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Kyoo.Api
 | 
					namespace Kyoo.Api
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -23,8 +23,8 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{slug}.{extension?}")]
 | 
							[HttpGet("{slug}.{extension}")]
 | 
				
			||||||
		[Authorize(Policy="Play")]
 | 
							[Permission(nameof(SubtitleApi), Kind.Read)]
 | 
				
			||||||
		public async Task<IActionResult> GetSubtitle(string slug, string extension)
 | 
							public async Task<IActionResult> GetSubtitle(string slug, string extension)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Track subtitle;
 | 
								Track subtitle;
 | 
				
			||||||
 | 
				
			|||||||
@ -2,17 +2,14 @@
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models.Attributes;
 | 
					 | 
				
			||||||
using Kyoo.Models.Exceptions;
 | 
					using Kyoo.Models.Exceptions;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using static Kyoo.Models.Attributes.PermissionAttribute;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Kyoo.Api
 | 
					namespace Kyoo.Api
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	[Route("api/task")]
 | 
						[Route("api/task")]
 | 
				
			||||||
	[Route("api/tasks")]
 | 
						[Route("api/tasks")]
 | 
				
			||||||
	[ApiController]
 | 
						[ApiController]
 | 
				
			||||||
	// [Authorize(Policy="Admin")]
 | 
					 | 
				
			||||||
	public class TaskApi : ControllerBase
 | 
						public class TaskApi : ControllerBase
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private readonly ITaskManager _taskManager;
 | 
							private readonly ITaskManager _taskManager;
 | 
				
			||||||
@ -24,7 +21,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpGet]
 | 
							[HttpGet]
 | 
				
			||||||
		[Permission("task", Kind.Read)]
 | 
							[Permission(nameof(TaskApi), Kind.Read)]
 | 
				
			||||||
		public ActionResult<ICollection<ITask>> GetTasks()
 | 
							public ActionResult<ICollection<ITask>> GetTasks()
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			return Ok(_taskManager.GetAllTasks());
 | 
								return Ok(_taskManager.GetAllTasks());
 | 
				
			||||||
@ -32,6 +29,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{taskSlug}")]
 | 
							[HttpGet("{taskSlug}")]
 | 
				
			||||||
		[HttpPut("{taskSlug}")]
 | 
							[HttpPut("{taskSlug}")]
 | 
				
			||||||
 | 
							[Permission(nameof(TaskApi), Kind.Create)]
 | 
				
			||||||
		public IActionResult RunTask(string taskSlug, [FromQuery] Dictionary<string, object> args)
 | 
							public IActionResult RunTask(string taskSlug, [FromQuery] Dictionary<string, object> args)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ using Kyoo.CommonApi;
 | 
				
			|||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models;
 | 
					using Kyoo.Models;
 | 
				
			||||||
using Kyoo.Models.Exceptions;
 | 
					using Kyoo.Models.Exceptions;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -13,6 +13,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
	[Route("api/track")]
 | 
						[Route("api/track")]
 | 
				
			||||||
	[Route("api/tracks")]
 | 
						[Route("api/tracks")]
 | 
				
			||||||
	[ApiController]
 | 
						[ApiController]
 | 
				
			||||||
 | 
						[PartialPermission(nameof(Track))]
 | 
				
			||||||
	public class TrackApi : CrudApi<Track>
 | 
						public class TrackApi : CrudApi<Track>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private readonly ILibraryManager _libraryManager;
 | 
							private readonly ILibraryManager _libraryManager;
 | 
				
			||||||
@ -24,7 +25,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{id:int}/episode")]
 | 
							[HttpGet("{id:int}/episode")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Episode>> GetEpisode(int id)
 | 
							public async Task<ActionResult<Episode>> GetEpisode(int id)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -38,7 +39,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{slug}/episode")]
 | 
							[HttpGet("{slug}/episode")]
 | 
				
			||||||
		[Authorize(Policy = "Read")]
 | 
							[PartialPermission(Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<Episode>> GetEpisode(string slug)
 | 
							public async Task<ActionResult<Episode>> GetEpisode(string slug)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Mvc;
 | 
				
			|||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Kyoo.Models.Exceptions;
 | 
					using Kyoo.Models.Exceptions;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc.Filters;
 | 
					using Microsoft.AspNetCore.Mvc.Filters;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Kyoo.Api
 | 
					namespace Kyoo.Api
 | 
				
			||||||
@ -44,7 +44,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("{slug}")]
 | 
							[HttpGet("{slug}")]
 | 
				
			||||||
		[HttpGet("direct/{slug}")]
 | 
							[HttpGet("direct/{slug}")]
 | 
				
			||||||
		[Authorize(Policy="Play")]
 | 
							// TODO enable the following line, this is disabled since the web app can't use bearers. [Permission("video", Kind.Read)]
 | 
				
			||||||
		public async Task<IActionResult> Direct(string slug)
 | 
							public async Task<IActionResult> Direct(string slug)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -59,7 +59,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpGet("transmux/{slug}/master.m3u8")]
 | 
							[HttpGet("transmux/{slug}/master.m3u8")]
 | 
				
			||||||
		[Authorize(Policy="Play")]
 | 
							[Permission("video", Kind.Read)]
 | 
				
			||||||
		public async Task<IActionResult> Transmux(string slug)
 | 
							public async Task<IActionResult> Transmux(string slug)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -78,7 +78,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpGet("transcode/{slug}/master.m3u8")]
 | 
							[HttpGet("transcode/{slug}/master.m3u8")]
 | 
				
			||||||
		[Authorize(Policy="Play")]
 | 
							[Permission("video", Kind.Read)]
 | 
				
			||||||
		public async Task<IActionResult> Transcode(string slug)
 | 
							public async Task<IActionResult> Transcode(string slug)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
@ -98,7 +98,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		
 | 
							
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("transmux/{episodeLink}/segments/{chunk}")]
 | 
							[HttpGet("transmux/{episodeLink}/segments/{chunk}")]
 | 
				
			||||||
		[Authorize(Policy="Play")]
 | 
							[Permission("video", Kind.Read)]
 | 
				
			||||||
		public IActionResult GetTransmuxedChunk(string episodeLink, string chunk)
 | 
							public IActionResult GetTransmuxedChunk(string episodeLink, string chunk)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			string path = Path.GetFullPath(Path.Combine(_transmuxPath, episodeLink));
 | 
								string path = Path.GetFullPath(Path.Combine(_transmuxPath, episodeLink));
 | 
				
			||||||
@ -107,7 +107,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		[HttpGet("transcode/{episodeLink}/segments/{chunk}")]
 | 
							[HttpGet("transcode/{episodeLink}/segments/{chunk}")]
 | 
				
			||||||
		[Authorize(Policy="Play")]
 | 
							[Permission("video", Kind.Read)]
 | 
				
			||||||
		public IActionResult GetTranscodedChunk(string episodeLink, string chunk)
 | 
							public IActionResult GetTranscodedChunk(string episodeLink, string chunk)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			string path = Path.GetFullPath(Path.Combine(_transcodePath, episodeLink));
 | 
								string path = Path.GetFullPath(Path.Combine(_transcodePath, episodeLink));
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
using Kyoo.Controllers;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
using Kyoo.Models;
 | 
					using Kyoo.Models;
 | 
				
			||||||
using Kyoo.Models.Exceptions;
 | 
					using Kyoo.Models.Exceptions;
 | 
				
			||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Models.Permissions;
 | 
				
			||||||
using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Kyoo.Api
 | 
					namespace Kyoo.Api
 | 
				
			||||||
@ -19,7 +19,7 @@ namespace Kyoo.Api
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[HttpGet("{slug}")]
 | 
							[HttpGet("{slug}")]
 | 
				
			||||||
		[Authorize(Policy="Read")]
 | 
							[Permission("video", Kind.Read)]
 | 
				
			||||||
		public async Task<ActionResult<WatchItem>> GetWatchItem(string slug)
 | 
							public async Task<ActionResult<WatchItem>> GetWatchItem(string slug)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			try
 | 
								try
 | 
				
			||||||
 | 
				
			|||||||
@ -32,8 +32,8 @@
 | 
				
			|||||||
      "password": "passphrase"
 | 
					      "password": "passphrase"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "permissions": {
 | 
					    "permissions": {
 | 
				
			||||||
      "default": [],
 | 
					      "default": ["read", "play", "write", "admin"],
 | 
				
			||||||
      "newUser": ["read", "play", "write", "admin"]
 | 
					      "newUser": ["read", "play", "write", "admin", "task.read"]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "profilePicturePath": "users/",
 | 
					    "profilePicturePath": "users/",
 | 
				
			||||||
    "clients": []
 | 
					    "clients": []
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user