diff --git a/Kyoo.Authentication/AuthenticationModule.cs b/Kyoo.Authentication/AuthenticationModule.cs index b21214ff..9018ba79 100644 --- a/Kyoo.Authentication/AuthenticationModule.cs +++ b/Kyoo.Authentication/AuthenticationModule.cs @@ -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(_configuration.GetSection(PermissionOption.Path)); services.Configure(_configuration.GetSection(CertificateOption.Path)); services.Configure(_configuration.GetSection(AuthenticationOption.Path)); + + + List 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() .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(); + services.AddSingleton(); DefaultCorsPolicyService cors = new(_loggerFactory.CreateLogger()) { diff --git a/Kyoo.Authentication/Controllers/AuthorizationValidatorHandler.cs b/Kyoo.Authentication/Controllers/AuthorizationValidatorHandler.cs deleted file mode 100644 index 1df1c86a..00000000 --- a/Kyoo.Authentication/Controllers/AuthorizationValidatorHandler.cs +++ /dev/null @@ -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 -{ - /// - /// The default IAuthorizationHandler implementation. - /// - public class AuthorizationValidatorHandler : AuthorizationHandler - { - /// - /// The permissions options to retrieve default permissions. - /// - private readonly IOptionsMonitor _options; - - /// - /// Create a new . - /// - /// The option containing default values. - public AuthorizationValidatorHandler(IOptionsMonitor options) - { - _options = options; - } - - - /// - 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 defaultPerms = _options.CurrentValue.Default; - if (defaultPerms?.Contains(requirement.Permission.ToLower()) == true) - context.Succeed(requirement); - } - - return Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/Kyoo.Authentication/Controllers/PremissionValidator.cs b/Kyoo.Authentication/Controllers/PremissionValidator.cs new file mode 100644 index 00000000..04f13ada --- /dev/null +++ b/Kyoo.Authentication/Controllers/PremissionValidator.cs @@ -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 +{ + /// + /// A permission validator to validate permission with user Permission array + /// or the default array from the configurations if the user is not logged. + /// + public class PermissionValidatorFactory : IPermissionValidator + { + /// + /// The permissions options to retrieve default permissions. + /// + private readonly IOptionsMonitor _options; + + /// + /// Create a new factory with the given options + /// + /// The option containing default values. + public PermissionValidatorFactory(IOptionsMonitor options) + { + _options = options; + } + + /// + public IFilterMetadata Create(PermissionAttribute attribute) + { + return new PermissionValidator(attribute.AsPermissionString(), _options); + } + + /// + public IFilterMetadata Create(PartialPermissionAttribute attribute) + { + return new PermissionValidator((object)attribute.Type ?? attribute.Kind, _options); + } + + /// + /// The authorization filter used by + /// + private class PermissionValidator : IAsyncAuthorizationFilter + { + /// + /// The permission to validate + /// + private readonly string _permission; + /// + /// Information about partial items. + /// + private readonly object _partialInfo; + /// + /// The permissions options to retrieve default permissions. + /// + private readonly IOptionsMonitor _options; + + /// + /// Create a new permission validator with the given options + /// + /// The permission to validate + /// The option containing default values. + public PermissionValidator(string permission, IOptionsMonitor options) + { + _permission = permission; + _options = options; + } + + /// + /// Create a new permission validator with the given options + /// + /// The partial permission to validate + /// The option containing default values. + public PermissionValidator(object partialInfo, IOptionsMonitor options) + { + _partialInfo = partialInfo; + _options = options; + } + + + /// + 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); + } + } + } + } +} \ No newline at end of file diff --git a/Kyoo.Authentication/Extensions.cs b/Kyoo.Authentication/Extensions.cs index 5c65fade..718a7a44 100644 --- a/Kyoo.Authentication/Extensions.cs +++ b/Kyoo.Authentication/Extensions.cs @@ -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))} }; } + + /// + /// Get the permissions of an user. + /// + /// The user + /// The list of permissions + public static ICollection GetPermissions(this ClaimsPrincipal user) + { + return user.Claims.FirstOrDefault(x => x.Type == "permissions")?.Value.Split(','); + } } } \ No newline at end of file diff --git a/Kyoo.Authentication/Models/AuthRequirement.cs b/Kyoo.Authentication/Models/AuthRequirement.cs deleted file mode 100644 index b74818b5..00000000 --- a/Kyoo.Authentication/Models/AuthRequirement.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.AspNetCore.Authorization; - -namespace Kyoo.Authentication -{ - /// - /// The requirement of Kyoo's authentication policies. - /// - public class AuthRequirement : IAuthorizationRequirement - { - /// - /// The name of the permission - /// - public string Permission { get; } - - /// - /// Create a new for the given permission. - /// - /// The permission needed - public AuthRequirement(string permission) - { - Permission = permission; - } - } -} \ No newline at end of file diff --git a/Kyoo.Authentication/Views/AccountApi.cs b/Kyoo.Authentication/Views/AccountApi.cs index d15ececc..59d964f0 100644 --- a/Kyoo.Authentication/Views/AccountApi.cs +++ b/Kyoo.Authentication/Views/AccountApi.cs @@ -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; diff --git a/Kyoo.Common/Models/Attributes/PermissionAttribute.cs b/Kyoo.Common/Models/Attributes/PermissionAttribute.cs index fd2285e9..b18ce8ee 100644 --- a/Kyoo.Common/Models/Attributes/PermissionAttribute.cs +++ b/Kyoo.Common/Models/Attributes/PermissionAttribute.cs @@ -1,23 +1,149 @@ using System; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; -namespace Kyoo.Models.Attributes +namespace Kyoo.Models.Permissions { + /// + /// The kind of permission needed. + /// + public enum Kind + { + Read, + Write, + Create, + Delete + } + /// /// Specify permissions needed for the API. /// - [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 - } - + /// + /// The needed permission as string. + /// + private readonly string _permission; + + /// + /// Ask a permission to run an action. + /// + /// + /// The type of the action + /// (if the type ends with api, it will be removed. This allow you to use nameof(YourApi)). + /// + /// The kind of permission needed public PermissionAttribute(string type, Kind permission) { - + if (type.EndsWith("API", StringComparison.OrdinalIgnoreCase)) + type = type[..^3]; + _permission = $"{type.ToLower()}.{permission.ToString().ToLower()}"; + } + + /// + public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) + { + return serviceProvider.GetRequiredService().Create(this); + } + + /// + public bool IsReusable => true; + + /// + /// Return this permission attribute as a string + /// + /// The string representation. + public string AsPermissionString() + { + return _permission; } } + + /// + /// Specify one part of a permissions needed for the API (the kind or the type). + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] + public class PartialPermissionAttribute : Attribute, IFilterFactory + { + /// + /// The needed permission type. + /// + public string Type { get; } + /// + /// The needed permission kind. + /// + public Kind Kind { get; } + + /// + /// Ask a permission to run an action. + /// + /// + /// 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. + /// + /// + /// The type of the action + /// (if the type ends with api, it will be removed. This allow you to use nameof(YourApi)). + /// + public PartialPermissionAttribute(string type) + { + if (type.EndsWith("API", StringComparison.OrdinalIgnoreCase)) + type = type[..^3]; + Type = type.ToLower(); + } + + /// + /// Ask a permission to run an action. + /// + /// + /// 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. + /// + /// The kind of permission needed + public PartialPermissionAttribute(Kind permission) + { + Kind = permission; + } + + /// + public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) + { + return serviceProvider.GetRequiredService().Create(this); + } + + /// + public bool IsReusable => true; + } + + + /// + /// A service to validate permissions + /// + public interface IPermissionValidator + { + /// + /// Create an IAuthorizationFilter that will be used to validate permissions. + /// This can registered with any lifetime. + /// + /// The permission attribute to validate + /// An authorization filter used to validate the permission + IFilterMetadata Create(PermissionAttribute attribute); + + /// + /// Create an IAuthorizationFilter that will be used to validate permissions. + /// This can registered with any lifetime. + /// + /// + /// A partial attribute to validate. See . + /// + /// An authorization filter used to validate the permission + IFilterMetadata Create(PartialPermissionAttribute attribute); + } } \ No newline at end of file diff --git a/Kyoo.CommonAPI/CrudApi.cs b/Kyoo.CommonAPI/CrudApi.cs index f14f10db..06713c66 100644 --- a/Kyoo.CommonAPI/CrudApi.cs +++ b/Kyoo.CommonAPI/CrudApi.cs @@ -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> 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> 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> GetCount([FromQuery] Dictionary where) { try @@ -60,7 +60,7 @@ namespace Kyoo.CommonApi } [HttpGet] - [Authorize(Policy = "Read")] + [PartialPermission(Kind.Read)] public virtual async Task>> GetAll([FromQuery] string sortBy, [FromQuery] int afterID, [FromQuery] Dictionary where, @@ -90,7 +90,7 @@ namespace Kyoo.CommonApi } [HttpPost] - [Authorize(Policy = "Write")] + [PartialPermission(Kind.Create)] public virtual async Task> Create([FromBody] T resource) { try @@ -109,7 +109,7 @@ namespace Kyoo.CommonApi } [HttpPut] - [Authorize(Policy = "Write")] + [PartialPermission(Kind.Write)] public virtual async Task> 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> 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> 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 Delete(int id) { try @@ -175,7 +175,7 @@ namespace Kyoo.CommonApi } [HttpDelete("{slug}")] - [Authorize(Policy = "Write")] + [PartialPermission(Kind.Delete)] public virtual async Task Delete(string slug) { try @@ -190,7 +190,7 @@ namespace Kyoo.CommonApi return Ok(); } - [Authorize(Policy = "Write")] + [PartialPermission(Kind.Delete)] public virtual async Task Delete(Dictionary where) { try diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index a612f7d5..a1bda566 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -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(); }); diff --git a/Kyoo/Views/CollectionApi.cs b/Kyoo/Views/CollectionApi.cs index 92bcc406..214cb60d 100644 --- a/Kyoo/Views/CollectionApi.cs +++ b/Kyoo/Views/CollectionApi.cs @@ -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 { 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>> 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>> GetShows(string slug, [FromQuery] string sortBy, [FromQuery] int afterID, @@ -66,7 +67,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(slug) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(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>> 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>> GetLibraries(string slug, [FromQuery] string sortBy, [FromQuery] int afterID, @@ -118,7 +119,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(slug) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(slug) == null) return NotFound(); return Page(resources, limit); } diff --git a/Kyoo/Views/EpisodeApi.cs b/Kyoo/Views/EpisodeApi.cs index 4935326a..c87a2704 100644 --- a/Kyoo/Views/EpisodeApi.cs +++ b/Kyoo/Views/EpisodeApi.cs @@ -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 { private readonly ILibraryManager _libraryManager; @@ -33,21 +34,27 @@ namespace Kyoo.Api } [HttpGet("{episodeID:int}/show")] - [Authorize(Policy = "Read")] + [PartialPermission(Kind.Read)] public async Task> GetShow(int episodeID) { - return await _libraryManager.Get(x => x.Episodes.Any(y => y.ID == episodeID)); + Show ret = await _libraryManager.GetOrDefault(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> GetShow(string showSlug, int seasonNumber, int episodeNumber) { - return await _libraryManager.Get(showSlug); + Show ret = await _libraryManager.GetOrDefault(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> GetShow(int showID, int seasonNumber, int episodeNumber) { Show ret = await _libraryManager.GetOrDefault(showID); @@ -57,7 +64,7 @@ namespace Kyoo.Api } [HttpGet("{episodeID:int}/season")] - [Authorize(Policy = "Read")] + [PartialPermission(Kind.Read)] public async Task> GetSeason(int episodeID) { Season ret = await _libraryManager.GetOrDefault(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> 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> 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>> 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>> 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>> GetEpisode(string slug, int seasonNumber, int episodeNumber, diff --git a/Kyoo/Views/GenreApi.cs b/Kyoo/Views/GenreApi.cs index c2df6b13..83ebb38d 100644 --- a/Kyoo/Views/GenreApi.cs +++ b/Kyoo/Views/GenreApi.cs @@ -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 { 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>> 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>> GetShows(string slug, [FromQuery] string sortBy, [FromQuery] int afterID, @@ -66,7 +67,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(slug) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(slug) == null) return NotFound(); return Page(resources, limit); } diff --git a/Kyoo/Views/LibraryApi.cs b/Kyoo/Views/LibraryApi.cs index 46707eec..239dcd8e 100644 --- a/Kyoo/Views/LibraryApi.cs +++ b/Kyoo/Views/LibraryApi.cs @@ -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 { private readonly ILibraryManager _libraryManager; @@ -26,7 +27,7 @@ namespace Kyoo.Api _taskManager = taskManager; } - [Authorize(Policy = "Write")] + [PartialPermission(Kind.Create)] public override async Task> Create(Library resource) { ActionResult 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>> 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>> GetShows(string slug, [FromQuery] string sortBy, [FromQuery] int afterID, @@ -77,7 +78,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(slug) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(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>> 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>> GetCollections(string slug, [FromQuery] string sortBy, [FromQuery] int afterID, @@ -129,7 +130,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(slug) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(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>> 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>> GetItems(string slug, [FromQuery] string sortBy, [FromQuery] int afterID, @@ -181,7 +182,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(slug) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(slug) == null) return NotFound(); return Page(resources, limit); } diff --git a/Kyoo/Views/LibraryItemApi.cs b/Kyoo/Views/LibraryItemApi.cs index 5c36cbc2..08367985 100644 --- a/Kyoo/Views/LibraryItemApi.cs +++ b/Kyoo/Views/LibraryItemApi.cs @@ -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>> GetAll([FromQuery] string sortBy, [FromQuery] int afterID, [FromQuery] Dictionary where, diff --git a/Kyoo/Views/PeopleApi.cs b/Kyoo/Views/PeopleApi.cs index 001caa33..724198e5 100644 --- a/Kyoo/Views/PeopleApi.cs +++ b/Kyoo/Views/PeopleApi.cs @@ -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 { 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>> 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>> GetRoles(string slug, [FromQuery] string sortBy, [FromQuery] int afterID, diff --git a/Kyoo/Views/ProviderApi.cs b/Kyoo/Views/ProviderApi.cs index 20d08f7b..133c15fd 100644 --- a/Kyoo/Views/ProviderApi.cs +++ b/Kyoo/Views/ProviderApi.cs @@ -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 + [PartialPermission(nameof(ProviderApi))] + public class ProviderApi : CrudApi { 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) diff --git a/Kyoo/Views/SearchApi.cs b/Kyoo/Views/SearchApi.cs index 008bf0f5..b78aa683 100644 --- a/Kyoo/Views/SearchApi.cs +++ b/Kyoo/Views/SearchApi.cs @@ -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> 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> SearchCollections(string query) { return _libraryManager.Search(query); @@ -44,7 +49,7 @@ namespace Kyoo.Api [HttpGet("show")] [HttpGet("shows")] - [Authorize(Policy="Read")] + [Permission(nameof(Show), Kind.Read)] public Task> SearchShows(string query) { return _libraryManager.Search(query); @@ -52,14 +57,14 @@ namespace Kyoo.Api [HttpGet("episode")] [HttpGet("episodes")] - [Authorize(Policy="Read")] + [Permission(nameof(Episode), Kind.Read)] public Task> SearchEpisodes(string query) { return _libraryManager.Search(query); } [HttpGet("people")] - [Authorize(Policy="Read")] + [Permission(nameof(People), Kind.Read)] public Task> SearchPeople(string query) { return _libraryManager.Search(query); @@ -67,7 +72,7 @@ namespace Kyoo.Api [HttpGet("genre")] [HttpGet("genres")] - [Authorize(Policy="Read")] + [Permission(nameof(Genre), Kind.Read)] public Task> SearchGenres(string query) { return _libraryManager.Search(query); @@ -75,7 +80,7 @@ namespace Kyoo.Api [HttpGet("studio")] [HttpGet("studios")] - [Authorize(Policy="Read")] + [Permission(nameof(Studio), Kind.Read)] public Task> SearchStudios(string query) { return _libraryManager.Search(query); diff --git a/Kyoo/Views/SeasonApi.cs b/Kyoo/Views/SeasonApi.cs index b578f350..1987e7c5 100644 --- a/Kyoo/Views/SeasonApi.cs +++ b/Kyoo/Views/SeasonApi.cs @@ -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 { 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>> 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>> 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>> 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> GetShow(int seasonID) { - return await _libraryManager.Get(x => x.Seasons.Any(y => y.ID == seasonID)); + Show ret = await _libraryManager.GetOrDefault(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> GetShow(string showSlug, int seasonNumber) { - return await _libraryManager.Get(showSlug); + Show ret = await _libraryManager.GetOrDefault(showSlug); + if (ret == null) + return NotFound(); + return ret; } [HttpGet("{showID:int}-s{seasonNumber:int}/show")] - [Authorize(Policy = "Read")] + [PartialPermission(Kind.Read)] public async Task> GetShow(int showID, int seasonNumber) { Show ret = await _libraryManager.GetOrDefault(showID); diff --git a/Kyoo/Views/ShowApi.cs b/Kyoo/Views/ShowApi.cs index b1d17538..8b73afd2 100644 --- a/Kyoo/Views/ShowApi.cs +++ b/Kyoo/Views/ShowApi.cs @@ -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 { 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>> 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>> 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>> 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>> 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>> 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>> 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>> 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>> 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> GetStudio(int showID) { try @@ -254,7 +255,7 @@ namespace Kyoo.Api } [HttpGet("{slug}/studio")] - [Authorize(Policy = "Read")] + [PartialPermission(Kind.Read)] public async Task> 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>> 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>> 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>> 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>> 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>> 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 GetFont(string showSlug, string slug) { try diff --git a/Kyoo/Views/StudioApi.cs b/Kyoo/Views/StudioApi.cs index ddf86092..66b6ca5d 100644 --- a/Kyoo/Views/StudioApi.cs +++ b/Kyoo/Views/StudioApi.cs @@ -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 { 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>> 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>> GetShows(string slug, [FromQuery] string sortBy, [FromQuery] int afterID, diff --git a/Kyoo/Views/SubtitleApi.cs b/Kyoo/Views/SubtitleApi.cs index 7052d9eb..5e680a73 100644 --- a/Kyoo/Views/SubtitleApi.cs +++ b/Kyoo/Views/SubtitleApi.cs @@ -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 GetSubtitle(string slug, string extension) { Track subtitle; diff --git a/Kyoo/Views/TaskApi.cs b/Kyoo/Views/TaskApi.cs index 91aa2a5d..b75033d7 100644 --- a/Kyoo/Views/TaskApi.cs +++ b/Kyoo/Views/TaskApi.cs @@ -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> 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 args) { try diff --git a/Kyoo/Views/TrackApi.cs b/Kyoo/Views/TrackApi.cs index 77125bdc..77f8669e 100644 --- a/Kyoo/Views/TrackApi.cs +++ b/Kyoo/Views/TrackApi.cs @@ -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 { private readonly ILibraryManager _libraryManager; @@ -24,7 +25,7 @@ namespace Kyoo.Api } [HttpGet("{id:int}/episode")] - [Authorize(Policy = "Read")] + [PartialPermission(Kind.Read)] public async Task> GetEpisode(int id) { try @@ -38,7 +39,7 @@ namespace Kyoo.Api } [HttpGet("{slug}/episode")] - [Authorize(Policy = "Read")] + [PartialPermission(Kind.Read)] public async Task> GetEpisode(string slug) { try diff --git a/Kyoo/Views/VideoApi.cs b/Kyoo/Views/VideoApi.cs index 498a93a2..050b82b4 100644 --- a/Kyoo/Views/VideoApi.cs +++ b/Kyoo/Views/VideoApi.cs @@ -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 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 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 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)); diff --git a/Kyoo/Views/WatchApi.cs b/Kyoo/Views/WatchApi.cs index 7418a6ae..38e65e21 100644 --- a/Kyoo/Views/WatchApi.cs +++ b/Kyoo/Views/WatchApi.cs @@ -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> GetWatchItem(string slug) { try diff --git a/Kyoo/settings.json b/Kyoo/settings.json index 34b24da9..60213e60 100644 --- a/Kyoo/settings.json +++ b/Kyoo/settings.json @@ -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": []