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.Collections.Generic;
using System.Linq;
using IdentityServer4.Extensions;
using IdentityServer4.Models;
using IdentityServer4.Services;
using Kyoo.Authentication.Models;
using Kyoo.Authentication.Views;
using Kyoo.Controllers;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
@ -86,18 +87,20 @@ namespace Kyoo.Authentication
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 remove unused/commented code, add documentation.
services.Configure<PermissionOption>(_configuration.GetSection(PermissionOption.Path));
services.Configure<CertificateOption>(_configuration.GetSection(CertificateOption.Path));
services.Configure<AuthenticationOption>(_configuration.GetSection(AuthenticationOption.Path));
List<Client> clients = new();
_configuration.GetSection("authentication:clients").Bind(clients);
CertificateOption certificateOptions = new();
_configuration.GetSection(CertificateOption.Path).Bind(certificateOptions);
services.AddIdentityServer(options =>
{
options.IssuerUri = publicUrl;
@ -108,11 +111,9 @@ namespace Kyoo.Authentication
.AddInMemoryIdentityResources(IdentityContext.GetIdentityResources())
.AddInMemoryApiScopes(IdentityContext.GetScopes())
.AddInMemoryApiResources(IdentityContext.GetApis())
.AddInMemoryClients(IdentityContext.GetClients())
.AddInMemoryClients(_configuration.GetSection("authentication:clients"))
.AddInMemoryClients(IdentityContext.GetClients().Concat(clients))
.AddProfileService<AccountApi>()
.AddSigninKeys(certificateOptions);
// TODO split scopes (kyoo.read should be task.read, video.read etc)
services.AddAuthentication()
.AddJwtBearer(options =>
@ -121,25 +122,7 @@ namespace Kyoo.Authentication
options.Audience = "kyoo";
options.RequireHttpsMetadata = false;
});
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>();
services.AddSingleton<IPermissionValidator, PermissionValidatorFactory>();
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.Linq;
using System.Security.Claims;
using IdentityModel;
using IdentityServer4;
@ -35,8 +36,19 @@ namespace Kyoo.Authentication
{
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.Models;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

View File

@ -1,23 +1,149 @@
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>
/// Specify permissions needed for the API.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class PermissionAttribute : Attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class PermissionAttribute : Attribute, IFilterFactory
{
public enum Kind
{
Read,
Write,
Admin
}
/// <summary>
/// The needed permission as string.
/// </summary>
private readonly string _permission;
/// <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)
{
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.Models;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
@ -26,7 +26,7 @@ namespace Kyoo.CommonApi
[HttpGet("{id:int}")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public virtual async Task<ActionResult<T>> Get(int id)
{
T ret = await _repository.GetOrDefault(id);
@ -36,7 +36,7 @@ namespace Kyoo.CommonApi
}
[HttpGet("{slug}")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public virtual async Task<ActionResult<T>> Get(string slug)
{
T ret = await _repository.Get(slug);
@ -46,7 +46,7 @@ namespace Kyoo.CommonApi
}
[HttpGet("count")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public virtual async Task<ActionResult<int>> GetCount([FromQuery] Dictionary<string, string> where)
{
try
@ -60,7 +60,7 @@ namespace Kyoo.CommonApi
}
[HttpGet]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public virtual async Task<ActionResult<Page<T>>> GetAll([FromQuery] string sortBy,
[FromQuery] int afterID,
[FromQuery] Dictionary<string, string> where,
@ -90,7 +90,7 @@ namespace Kyoo.CommonApi
}
[HttpPost]
[Authorize(Policy = "Write")]
[PartialPermission(Kind.Create)]
public virtual async Task<ActionResult<T>> Create([FromBody] T resource)
{
try
@ -109,7 +109,7 @@ namespace Kyoo.CommonApi
}
[HttpPut]
[Authorize(Policy = "Write")]
[PartialPermission(Kind.Write)]
public virtual async Task<ActionResult<T>> Edit([FromQuery] bool resetOld, [FromBody] T resource)
{
try
@ -128,7 +128,7 @@ namespace Kyoo.CommonApi
}
[HttpPut("{id:int}")]
[Authorize(Policy = "Write")]
[PartialPermission(Kind.Write)]
public virtual async Task<ActionResult<T>> Edit(int id, [FromQuery] bool resetOld, [FromBody] T resource)
{
resource.ID = id;
@ -143,7 +143,7 @@ namespace Kyoo.CommonApi
}
[HttpPut("{slug}")]
[Authorize(Policy = "Write")]
[PartialPermission(Kind.Write)]
public virtual async Task<ActionResult<T>> Edit(string slug, [FromQuery] bool resetOld, [FromBody] T resource)
{
try
@ -159,7 +159,7 @@ namespace Kyoo.CommonApi
}
[HttpDelete("{id:int}")]
[Authorize(Policy = "Write")]
[PartialPermission(Kind.Delete)]
public virtual async Task<IActionResult> Delete(int id)
{
try
@ -175,7 +175,7 @@ namespace Kyoo.CommonApi
}
[HttpDelete("{slug}")]
[Authorize(Policy = "Write")]
[PartialPermission(Kind.Delete)]
public virtual async Task<IActionResult> Delete(string slug)
{
try
@ -190,7 +190,7 @@ namespace Kyoo.CommonApi
return Ok();
}
[Authorize(Policy = "Write")]
[PartialPermission(Kind.Delete)]
public virtual async Task<IActionResult> Delete(Dictionary<string, string> where)
{
try

View File

@ -7,6 +7,7 @@ using Kyoo.Postgresql;
using Kyoo.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Configuration;
@ -131,7 +132,7 @@ namespace Kyoo
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 System.Threading.Tasks;
using Kyoo.CommonApi;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.Extensions.Configuration;
namespace Kyoo.Api
@ -14,6 +14,7 @@ namespace Kyoo.Api
[Route("api/collection")]
[Route("api/collections")]
[ApiController]
[PartialPermission(nameof(CollectionApi))]
public class CollectionApi : CrudApi<Collection>
{
private readonly ILibraryManager _libraryManager;
@ -26,7 +27,7 @@ namespace Kyoo.Api
[HttpGet("{id:int}/show")]
[HttpGet("{id:int}/shows")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Show>>> GetShows(int id,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -52,7 +53,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/show")]
[HttpGet("{slug}/shows")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Show>>> GetShows(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -66,7 +67,7 @@ namespace Kyoo.Api
new Sort<Show>(sortBy),
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 Page(resources, limit);
}
@ -78,7 +79,7 @@ namespace Kyoo.Api
[HttpGet("{id:int}/library")]
[HttpGet("{id:int}/libraries")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Library>>> GetLibraries(int id,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -104,7 +105,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/library")]
[HttpGet("{slug}/libraries")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Library>>> GetLibraries(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -118,7 +119,7 @@ namespace Kyoo.Api
new Sort<Library>(sortBy),
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 Page(resources, limit);
}

View File

@ -7,7 +7,7 @@ using System.Threading.Tasks;
using Kyoo.CommonApi;
using Kyoo.Controllers;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.Extensions.Configuration;
namespace Kyoo.Api
@ -15,6 +15,7 @@ namespace Kyoo.Api
[Route("api/episode")]
[Route("api/episodes")]
[ApiController]
[PartialPermission(nameof(EpisodeApi))]
public class EpisodeApi : CrudApi<Episode>
{
private readonly ILibraryManager _libraryManager;
@ -33,21 +34,27 @@ namespace Kyoo.Api
}
[HttpGet("{episodeID:int}/show")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
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")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
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")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Show>> GetShow(int showID, int seasonNumber, int episodeNumber)
{
Show ret = await _libraryManager.GetOrDefault<Show>(showID);
@ -57,7 +64,7 @@ namespace Kyoo.Api
}
[HttpGet("{episodeID:int}/season")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Season>> GetSeason(int 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")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Season>> GetSeason(string showSlug, int seasonNumber, int episodeNumber)
{
try
@ -81,7 +88,7 @@ namespace Kyoo.Api
}
[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)
{
try
@ -96,7 +103,7 @@ namespace Kyoo.Api
[HttpGet("{episodeID:int}/track")]
[HttpGet("{episodeID:int}/tracks")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Track>>> GetEpisode(int episodeID,
[FromQuery] string sortBy,
[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}/tracks")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Track>>> GetEpisode(int showID,
int seasonNumber,
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}/tracks")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Track>>> GetEpisode(string slug,
int seasonNumber,
int episodeNumber,

View File

@ -5,7 +5,7 @@ using System.Threading.Tasks;
using Kyoo.CommonApi;
using Kyoo.Controllers;
using Kyoo.Models;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
@ -14,6 +14,7 @@ namespace Kyoo.Api
[Route("api/genre")]
[Route("api/genres")]
[ApiController]
[PartialPermission(nameof(GenreApi))]
public class GenreApi : CrudApi<Genre>
{
private readonly ILibraryManager _libraryManager;
@ -26,7 +27,7 @@ namespace Kyoo.Api
[HttpGet("{id:int}/show")]
[HttpGet("{id:int}/shows")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Show>>> GetShows(int id,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -52,7 +53,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/show")]
[HttpGet("{slug}/shows")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Show>>> GetShows(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -66,7 +67,7 @@ namespace Kyoo.Api
new Sort<Show>(sortBy),
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 Page(resources, limit);
}

View File

@ -6,7 +6,7 @@ using Kyoo.Models;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Kyoo.CommonApi;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.Extensions.Configuration;
namespace Kyoo.Api
@ -14,6 +14,7 @@ namespace Kyoo.Api
[Route("api/library")]
[Route("api/libraries")]
[ApiController]
[PartialPermission(nameof(LibraryAPI))]
public class LibraryAPI : CrudApi<Library>
{
private readonly ILibraryManager _libraryManager;
@ -26,7 +27,7 @@ namespace Kyoo.Api
_taskManager = taskManager;
}
[Authorize(Policy = "Write")]
[PartialPermission(Kind.Create)]
public override async Task<ActionResult<Library>> Create(Library resource)
{
ActionResult<Library> result = await base.Create(resource);
@ -37,7 +38,7 @@ namespace Kyoo.Api
[HttpGet("{id:int}/show")]
[HttpGet("{id:int}/shows")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Show>>> GetShows(int id,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -63,7 +64,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/show")]
[HttpGet("{slug}/shows")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Show>>> GetShows(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -77,7 +78,7 @@ namespace Kyoo.Api
new Sort<Show>(sortBy),
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 Page(resources, limit);
}
@ -89,7 +90,7 @@ namespace Kyoo.Api
[HttpGet("{id:int}/collection")]
[HttpGet("{id:int}/collections")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Collection>>> GetCollections(int id,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -115,7 +116,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/collection")]
[HttpGet("{slug}/collections")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Collection>>> GetCollections(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -129,7 +130,7 @@ namespace Kyoo.Api
new Sort<Collection>(sortBy),
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 Page(resources, limit);
}
@ -141,7 +142,7 @@ namespace Kyoo.Api
[HttpGet("{id:int}/item")]
[HttpGet("{id:int}/items")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<LibraryItem>>> GetItems(int id,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -167,7 +168,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/item")]
[HttpGet("{slug}/items")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<LibraryItem>>> GetItems(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -181,7 +182,7 @@ namespace Kyoo.Api
new Sort<LibraryItem>(sortBy),
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 Page(resources, limit);
}

View File

@ -6,7 +6,7 @@ using Kyoo.CommonApi;
using Kyoo.Controllers;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
@ -29,7 +29,7 @@ namespace Kyoo.Api
}
[HttpGet]
[Authorize(Policy = "Read")]
[Permission(nameof(LibraryItemApi), Kind.Read)]
public async Task<ActionResult<Page<LibraryItem>>> GetAll([FromQuery] string sortBy,
[FromQuery] int afterID,
[FromQuery] Dictionary<string, string> where,

View File

@ -5,7 +5,7 @@ using Kyoo.CommonApi;
using Kyoo.Controllers;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
@ -13,6 +13,7 @@ namespace Kyoo.Api
{
[Route("api/people")]
[ApiController]
[PartialPermission(nameof(PeopleApi))]
public class PeopleApi : CrudApi<People>
{
private readonly ILibraryManager _libraryManager;
@ -32,7 +33,7 @@ namespace Kyoo.Api
[HttpGet("{id:int}/role")]
[HttpGet("{id:int}/roles")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<PeopleRole>>> GetRoles(int id,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -60,7 +61,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/role")]
[HttpGet("{slug}/roles")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<PeopleRole>>> GetRoles(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,

View File

@ -2,6 +2,7 @@ using System.Threading.Tasks;
using Kyoo.CommonApi;
using Kyoo.Controllers;
using Kyoo.Models;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
@ -10,13 +11,14 @@ namespace Kyoo.Api
[Route("api/provider")]
[Route("api/providers")]
[ApiController]
public class ProviderAPI : CrudApi<Provider>
[PartialPermission(nameof(ProviderApi))]
public class ProviderApi : CrudApi<Provider>
{
private readonly IThumbnailsManager _thumbnails;
private readonly ILibraryManager _libraryManager;
private readonly IFileManager _files;
public ProviderAPI(ILibraryManager libraryManager,
public ProviderApi(ILibraryManager libraryManager,
IConfiguration config,
IFileManager files,
IThumbnailsManager thumbnails)

View File

@ -2,7 +2,7 @@
using System.Threading.Tasks;
using Kyoo.Controllers;
using Kyoo.Models;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Api
@ -19,7 +19,12 @@ namespace Kyoo.Api
}
[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)
{
return new SearchResult
@ -36,7 +41,7 @@ namespace Kyoo.Api
[HttpGet("collection")]
[HttpGet("collections")]
[Authorize(Policy="Read")]
[Permission(nameof(Collection), Kind.Read)]
public Task<ICollection<Collection>> SearchCollections(string query)
{
return _libraryManager.Search<Collection>(query);
@ -44,7 +49,7 @@ namespace Kyoo.Api
[HttpGet("show")]
[HttpGet("shows")]
[Authorize(Policy="Read")]
[Permission(nameof(Show), Kind.Read)]
public Task<ICollection<Show>> SearchShows(string query)
{
return _libraryManager.Search<Show>(query);
@ -52,14 +57,14 @@ namespace Kyoo.Api
[HttpGet("episode")]
[HttpGet("episodes")]
[Authorize(Policy="Read")]
[Permission(nameof(Episode), Kind.Read)]
public Task<ICollection<Episode>> SearchEpisodes(string query)
{
return _libraryManager.Search<Episode>(query);
}
[HttpGet("people")]
[Authorize(Policy="Read")]
[Permission(nameof(People), Kind.Read)]
public Task<ICollection<People>> SearchPeople(string query)
{
return _libraryManager.Search<People>(query);
@ -67,7 +72,7 @@ namespace Kyoo.Api
[HttpGet("genre")]
[HttpGet("genres")]
[Authorize(Policy="Read")]
[Permission(nameof(Genre), Kind.Read)]
public Task<ICollection<Genre>> SearchGenres(string query)
{
return _libraryManager.Search<Genre>(query);
@ -75,7 +80,7 @@ namespace Kyoo.Api
[HttpGet("studio")]
[HttpGet("studios")]
[Authorize(Policy="Read")]
[Permission(nameof(Studio), Kind.Read)]
public Task<ICollection<Studio>> SearchStudios(string query)
{
return _libraryManager.Search<Studio>(query);

View File

@ -4,9 +4,9 @@ using System.Threading.Tasks;
using Kyoo.CommonApi;
using Kyoo.Controllers;
using Kyoo.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Linq;
using Kyoo.Models.Permissions;
using Microsoft.Extensions.Configuration;
namespace Kyoo.Api
@ -14,6 +14,7 @@ namespace Kyoo.Api
[Route("api/season")]
[Route("api/seasons")]
[ApiController]
[PartialPermission(nameof(SeasonApi))]
public class SeasonApi : CrudApi<Season>
{
private readonly ILibraryManager _libraryManager;
@ -33,7 +34,7 @@ namespace Kyoo.Api
[HttpGet("{seasonID:int}/episode")]
[HttpGet("{seasonID:int}/episodes")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Episode>>> GetEpisode(int seasonID,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -59,7 +60,7 @@ namespace Kyoo.Api
[HttpGet("{showSlug}-s{seasonNumber:int}/episode")]
[HttpGet("{showSlug}-s{seasonNumber:int}/episodes")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Episode>>> GetEpisode(string showSlug,
int seasonNumber,
[FromQuery] string sortBy,
@ -87,7 +88,7 @@ namespace Kyoo.Api
[HttpGet("{showID:int}-s{seasonNumber:int}/episode")]
[HttpGet("{showID:int}-s{seasonNumber:int}/episodes")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Episode>>> GetEpisode(int showID,
int seasonNumber,
[FromQuery] string sortBy,
@ -113,21 +114,27 @@ namespace Kyoo.Api
}
[HttpGet("{seasonID:int}/show")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
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")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
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")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Show>> GetShow(int showID, int seasonNumber)
{
Show ret = await _libraryManager.GetOrDefault<Show>(showID);

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
using Kyoo.CommonApi;
using Kyoo.Controllers;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.Extensions.Configuration;
namespace Kyoo.Api
@ -16,6 +16,7 @@ namespace Kyoo.Api
[Route("api/show")]
[Route("api/shows")]
[ApiController]
[PartialPermission(nameof(ShowApi))]
public class ShowApi : CrudApi<Show>
{
private readonly ILibraryManager _libraryManager;
@ -35,7 +36,7 @@ namespace Kyoo.Api
[HttpGet("{showID:int}/season")]
[HttpGet("{showID:int}/seasons")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Season>>> GetSeasons(int showID,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -61,7 +62,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/season")]
[HttpGet("{slug}/seasons")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Season>>> GetSeasons(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -87,7 +88,7 @@ namespace Kyoo.Api
[HttpGet("{showID:int}/episode")]
[HttpGet("{showID:int}/episodes")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Episode>>> GetEpisodes(int showID,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -113,7 +114,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/episode")]
[HttpGet("{slug}/episodes")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Episode>>> GetEpisodes(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -138,7 +139,7 @@ namespace Kyoo.Api
}
[HttpGet("{showID:int}/people")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<PeopleRole>>> GetPeople(int showID,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -163,7 +164,7 @@ namespace Kyoo.Api
}
[HttpGet("{slug}/people")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<PeopleRole>>> GetPeople(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -189,7 +190,7 @@ namespace Kyoo.Api
[HttpGet("{showID:int}/genre")]
[HttpGet("{showID:int}/genres")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Genre>>> GetGenres(int showID,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -215,7 +216,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/genre")]
[HttpGet("{slug}/genres")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Genre>>> GetGenre(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -240,7 +241,7 @@ namespace Kyoo.Api
}
[HttpGet("{showID:int}/studio")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Studio>> GetStudio(int showID)
{
try
@ -254,7 +255,7 @@ namespace Kyoo.Api
}
[HttpGet("{slug}/studio")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Studio>> GetStudio(string slug)
{
try
@ -269,7 +270,7 @@ namespace Kyoo.Api
[HttpGet("{showID:int}/library")]
[HttpGet("{showID:int}/libraries")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Library>>> GetLibraries(int showID,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -295,7 +296,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/library")]
[HttpGet("{slug}/libraries")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Library>>> GetLibraries(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -321,7 +322,7 @@ namespace Kyoo.Api
[HttpGet("{showID:int}/collection")]
[HttpGet("{showID:int}/collections")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Collection>>> GetCollections(int showID,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -347,7 +348,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/collection")]
[HttpGet("{slug}/collections")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Collection>>> GetCollections(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -373,7 +374,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/font")]
[HttpGet("{slug}/fonts")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Dictionary<string, string>>> GetFonts(string slug)
{
try
@ -392,7 +393,7 @@ namespace Kyoo.Api
[HttpGet("{showSlug}/font/{slug}")]
[HttpGet("{showSlug}/fonts/{slug}")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<IActionResult> GetFont(string showSlug, string slug)
{
try

View File

@ -5,7 +5,7 @@ using System.Threading.Tasks;
using Kyoo.CommonApi;
using Kyoo.Controllers;
using Kyoo.Models;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
@ -14,6 +14,7 @@ namespace Kyoo.Api
[Route("api/studio")]
[Route("api/studios")]
[ApiController]
[PartialPermission(nameof(ShowApi))]
public class StudioAPI : CrudApi<Studio>
{
private readonly ILibraryManager _libraryManager;
@ -26,7 +27,7 @@ namespace Kyoo.Api
[HttpGet("{id:int}/show")]
[HttpGet("{id:int}/shows")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Show>>> GetShows(int id,
[FromQuery] string sortBy,
[FromQuery] int afterID,
@ -52,7 +53,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/show")]
[HttpGet("{slug}/shows")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Page<Show>>> GetShows(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Kyoo.Controllers;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
namespace Kyoo.Api
{
@ -23,8 +23,8 @@ namespace Kyoo.Api
}
[HttpGet("{slug}.{extension?}")]
[Authorize(Policy="Play")]
[HttpGet("{slug}.{extension}")]
[Permission(nameof(SubtitleApi), Kind.Read)]
public async Task<IActionResult> GetSubtitle(string slug, string extension)
{
Track subtitle;

View File

@ -2,17 +2,14 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authorization;
using static Kyoo.Models.Attributes.PermissionAttribute;
using Kyoo.Models.Permissions;
namespace Kyoo.Api
{
[Route("api/task")]
[Route("api/tasks")]
[ApiController]
// [Authorize(Policy="Admin")]
public class TaskApi : ControllerBase
{
private readonly ITaskManager _taskManager;
@ -24,7 +21,7 @@ namespace Kyoo.Api
[HttpGet]
[Permission("task", Kind.Read)]
[Permission(nameof(TaskApi), Kind.Read)]
public ActionResult<ICollection<ITask>> GetTasks()
{
return Ok(_taskManager.GetAllTasks());
@ -32,6 +29,7 @@ namespace Kyoo.Api
[HttpGet("{taskSlug}")]
[HttpPut("{taskSlug}")]
[Permission(nameof(TaskApi), Kind.Create)]
public IActionResult RunTask(string taskSlug, [FromQuery] Dictionary<string, object> args)
{
try

View File

@ -4,7 +4,7 @@ using Kyoo.CommonApi;
using Kyoo.Controllers;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
@ -13,6 +13,7 @@ namespace Kyoo.Api
[Route("api/track")]
[Route("api/tracks")]
[ApiController]
[PartialPermission(nameof(Track))]
public class TrackApi : CrudApi<Track>
{
private readonly ILibraryManager _libraryManager;
@ -24,7 +25,7 @@ namespace Kyoo.Api
}
[HttpGet("{id:int}/episode")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Episode>> GetEpisode(int id)
{
try
@ -38,7 +39,7 @@ namespace Kyoo.Api
}
[HttpGet("{slug}/episode")]
[Authorize(Policy = "Read")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Episode>> GetEpisode(string slug)
{
try

View File

@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System.Threading.Tasks;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Kyoo.Api
@ -44,7 +44,7 @@ namespace Kyoo.Api
[HttpGet("{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)
{
try
@ -59,7 +59,7 @@ namespace Kyoo.Api
}
[HttpGet("transmux/{slug}/master.m3u8")]
[Authorize(Policy="Play")]
[Permission("video", Kind.Read)]
public async Task<IActionResult> Transmux(string slug)
{
try
@ -78,7 +78,7 @@ namespace Kyoo.Api
}
[HttpGet("transcode/{slug}/master.m3u8")]
[Authorize(Policy="Play")]
[Permission("video", Kind.Read)]
public async Task<IActionResult> Transcode(string slug)
{
try
@ -98,7 +98,7 @@ namespace Kyoo.Api
[HttpGet("transmux/{episodeLink}/segments/{chunk}")]
[Authorize(Policy="Play")]
[Permission("video", Kind.Read)]
public IActionResult GetTransmuxedChunk(string episodeLink, string chunk)
{
string path = Path.GetFullPath(Path.Combine(_transmuxPath, episodeLink));
@ -107,7 +107,7 @@ namespace Kyoo.Api
}
[HttpGet("transcode/{episodeLink}/segments/{chunk}")]
[Authorize(Policy="Play")]
[Permission("video", Kind.Read)]
public IActionResult GetTranscodedChunk(string episodeLink, string chunk)
{
string path = Path.GetFullPath(Path.Combine(_transcodePath, episodeLink));

View File

@ -2,7 +2,7 @@
using Kyoo.Controllers;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Api
@ -19,7 +19,7 @@ namespace Kyoo.Api
}
[HttpGet("{slug}")]
[Authorize(Policy="Read")]
[Permission("video", Kind.Read)]
public async Task<ActionResult<WatchItem>> GetWatchItem(string slug)
{
try

View File

@ -32,8 +32,8 @@
"password": "passphrase"
},
"permissions": {
"default": [],
"newUser": ["read", "play", "write", "admin"]
"default": ["read", "play", "write", "admin"],
"newUser": ["read", "play", "write", "admin", "task.read"]
},
"profilePicturePath": "users/",
"clients": []