mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Reworking authorization handling
This commit is contained in:
parent
765bd061b7
commit
71c18092e5
@ -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>())
|
||||
{
|
||||
|
@ -1,51 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityServer4.Extensions;
|
||||
using Kyoo.Authentication.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Kyoo.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// The default IAuthorizationHandler implementation.
|
||||
/// </summary>
|
||||
public class AuthorizationValidatorHandler : AuthorizationHandler<AuthRequirement>
|
||||
{
|
||||
/// <summary>
|
||||
/// The permissions options to retrieve default permissions.
|
||||
/// </summary>
|
||||
private readonly IOptionsMonitor<PermissionOption> _options;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="AuthorizationValidatorHandler"/>.
|
||||
/// </summary>
|
||||
/// <param name="options">The option containing default values.</param>
|
||||
public AuthorizationValidatorHandler(IOptionsMonitor<PermissionOption> options)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthRequirement requirement)
|
||||
{
|
||||
if (context.User.IsAuthenticated())
|
||||
{
|
||||
Claim perms = context.User.Claims.FirstOrDefault(x => x.Type == "permissions");
|
||||
if (perms != null && perms.Value.Split(",").Contains(requirement.Permission.ToLower()))
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
else
|
||||
{
|
||||
ICollection<string> defaultPerms = _options.CurrentValue.Default;
|
||||
if (defaultPerms?.Contains(requirement.Permission.ToLower()) == true)
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
126
Kyoo.Authentication/Controllers/PremissionValidator.cs
Normal file
126
Kyoo.Authentication/Controllers/PremissionValidator.cs
Normal file
@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Authentication.Models;
|
||||
using Kyoo.Models.Permissions;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Kyoo.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// A permission validator to validate permission with user Permission array
|
||||
/// or the default array from the configurations if the user is not logged.
|
||||
/// </summary>
|
||||
public class PermissionValidatorFactory : IPermissionValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// The permissions options to retrieve default permissions.
|
||||
/// </summary>
|
||||
private readonly IOptionsMonitor<PermissionOption> _options;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new factory with the given options
|
||||
/// </summary>
|
||||
/// <param name="options">The option containing default values.</param>
|
||||
public PermissionValidatorFactory(IOptionsMonitor<PermissionOption> options)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IFilterMetadata Create(PermissionAttribute attribute)
|
||||
{
|
||||
return new PermissionValidator(attribute.AsPermissionString(), _options);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IFilterMetadata Create(PartialPermissionAttribute attribute)
|
||||
{
|
||||
return new PermissionValidator((object)attribute.Type ?? attribute.Kind, _options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The authorization filter used by <see cref="PermissionValidatorFactory"/>
|
||||
/// </summary>
|
||||
private class PermissionValidator : IAsyncAuthorizationFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// The permission to validate
|
||||
/// </summary>
|
||||
private readonly string _permission;
|
||||
/// <summary>
|
||||
/// Information about partial items.
|
||||
/// </summary>
|
||||
private readonly object _partialInfo;
|
||||
/// <summary>
|
||||
/// The permissions options to retrieve default permissions.
|
||||
/// </summary>
|
||||
private readonly IOptionsMonitor<PermissionOption> _options;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new permission validator with the given options
|
||||
/// </summary>
|
||||
/// <param name="permission">The permission to validate</param>
|
||||
/// <param name="options">The option containing default values.</param>
|
||||
public PermissionValidator(string permission, IOptionsMonitor<PermissionOption> options)
|
||||
{
|
||||
_permission = permission;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new permission validator with the given options
|
||||
/// </summary>
|
||||
/// <param name="partialInfo">The partial permission to validate</param>
|
||||
/// <param name="options">The option containing default values.</param>
|
||||
public PermissionValidator(object partialInfo, IOptionsMonitor<PermissionOption> options)
|
||||
{
|
||||
_partialInfo = partialInfo;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||
{
|
||||
string permission = _permission;
|
||||
|
||||
if (_partialInfo != null)
|
||||
{
|
||||
switch (context.HttpContext.Items["PermissionType"])
|
||||
{
|
||||
case string perm when _partialInfo is Kind kind:
|
||||
permission = $"{perm}.{kind.ToString().ToLower()}";
|
||||
break;
|
||||
case Kind kind when _partialInfo is string partial:
|
||||
permission = $"{partial}.{kind.ToString().ToLower()}";
|
||||
break;
|
||||
case null:
|
||||
context.HttpContext.Items["PermissionType"] = _partialInfo;
|
||||
return;
|
||||
default:
|
||||
throw new ArgumentException("Multiple non-matching partial permission attribute " +
|
||||
"are not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
AuthenticateResult res = await context.HttpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);
|
||||
if (res.Succeeded)
|
||||
{
|
||||
if (res.Principal.GetPermissions().All(x => x != permission))
|
||||
context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (res.Failure != null || _options.CurrentValue.Default.All(x => x != permission))
|
||||
context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.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(',');
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Kyoo.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// The requirement of Kyoo's authentication policies.
|
||||
/// </summary>
|
||||
public class AuthRequirement : IAuthorizationRequirement
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the permission
|
||||
/// </summary>
|
||||
public string Permission { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="AuthRequirement"/> for the given permission.
|
||||
/// </summary>
|
||||
/// <param name="permission">The permission needed</param>
|
||||
public AuthRequirement(string permission)
|
||||
{
|
||||
Permission = permission;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,8 +13,8 @@ using Kyoo.Authentication.Models.DTO;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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": []
|
||||
|
Loading…
x
Reference in New Issue
Block a user