Reworking authorization handling

This commit is contained in:
Zoe Roux 2021-05-11 00:13:37 +02:00
parent 765bd061b7
commit 71c18092e5
26 changed files with 427 additions and 228 deletions

View File

@ -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>())
{ {

View File

@ -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;
}
}
}

View 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);
}
}
}
}
}

View File

@ -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(',');
}
} }
} }

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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);
}
} }

View File

@ -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

View File

@ -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();
}); });

View File

@ -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);
} }

View File

@ -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,

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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,

View File

@ -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,

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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

View File

@ -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": []