From 21e354bf007789951d675efc723f990536f44c5b Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Tue, 11 May 2021 01:20:25 +0200 Subject: [PATCH] Handling overall permissions --- .../Controllers/PremissionValidator.cs | 47 +++++++++++++------ .../Models/Attributes/PermissionAttribute.cs | 11 +++-- .../PassthroughPermissionValidator.cs | 35 ++++++++++++++ Kyoo/CoreModule.cs | 5 ++ Kyoo/Kyoo.csproj | 2 +- Kyoo/Startup.cs | 5 +- Kyoo/settings.json | 4 +- 7 files changed, 85 insertions(+), 24 deletions(-) create mode 100644 Kyoo/Controllers/PassthroughPermissionValidator.cs diff --git a/Kyoo.Authentication/Controllers/PremissionValidator.cs b/Kyoo.Authentication/Controllers/PremissionValidator.cs index 04f13ada..dc60faa7 100644 --- a/Kyoo.Authentication/Controllers/PremissionValidator.cs +++ b/Kyoo.Authentication/Controllers/PremissionValidator.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Kyoo.Authentication.Models; @@ -35,7 +36,7 @@ namespace Kyoo.Authentication /// public IFilterMetadata Create(PermissionAttribute attribute) { - return new PermissionValidator(attribute.AsPermissionString(), _options); + return new PermissionValidator(attribute.Type, attribute.Kind, _options); } /// @@ -54,9 +55,9 @@ namespace Kyoo.Authentication /// private readonly string _permission; /// - /// Information about partial items. + /// The kind of permission needed /// - private readonly object _partialInfo; + private readonly Kind? _kind; /// /// The permissions options to retrieve default permissions. /// @@ -66,10 +67,12 @@ namespace Kyoo.Authentication /// Create a new permission validator with the given options /// /// The permission to validate + /// The kind of permission needed /// The option containing default values. - public PermissionValidator(string permission, IOptionsMonitor options) + public PermissionValidator(string permission, Kind kind, IOptionsMonitor options) { _permission = permission; + _kind = kind; _options = options; } @@ -80,7 +83,12 @@ namespace Kyoo.Authentication /// The option containing default values. public PermissionValidator(object partialInfo, IOptionsMonitor options) { - _partialInfo = partialInfo; + if (partialInfo is Kind kind) + _kind = kind; + else if (partialInfo is string perm) + _permission = perm; + else + throw new ArgumentException($"{nameof(partialInfo)} can only be a permission string or a kind."); _options = options; } @@ -89,35 +97,46 @@ namespace Kyoo.Authentication public async Task OnAuthorizationAsync(AuthorizationFilterContext context) { string permission = _permission; + Kind? kind = _kind; - if (_partialInfo != null) + if (permission == null || kind == null) { switch (context.HttpContext.Items["PermissionType"]) { - case string perm when _partialInfo is Kind kind: - permission = $"{perm}.{kind.ToString().ToLower()}"; + case string perm: + permission = perm; break; - case Kind kind when _partialInfo is string partial: - permission = $"{partial}.{kind.ToString().ToLower()}"; + case Kind kin: + kind = kin; break; - case null: - context.HttpContext.Items["PermissionType"] = _partialInfo; + case null when kind != null: + context.HttpContext.Items["PermissionType"] = kind; + return; + case null when permission != null: + context.HttpContext.Items["PermissionType"] = permission; return; default: throw new ArgumentException("Multiple non-matching partial permission attribute " + "are not supported."); } + if (permission == null || kind == null) + throw new ArgumentException("The permission type or kind is still missing after two partial " + + "permission attributes, this is unsupported."); } + string permStr = $"{permission.ToLower()}.{kind.ToString()!.ToLower()}"; + string overallStr = $"overall.{kind.ToString()!.ToLower()}"; AuthenticateResult res = await context.HttpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme); if (res.Succeeded) { - if (res.Principal.GetPermissions().All(x => x != permission)) + ICollection permissions = res.Principal.GetPermissions(); + if (permissions.All(x => x != permStr && x != overallStr)) context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden); } else { - if (res.Failure != null || _options.CurrentValue.Default.All(x => x != permission)) + ICollection permissions = _options.CurrentValue.Default ?? Array.Empty(); + if (res.Failure != null || permissions.All(x => x != permStr && x != overallStr)) context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized); } } diff --git a/Kyoo.Common/Models/Attributes/PermissionAttribute.cs b/Kyoo.Common/Models/Attributes/PermissionAttribute.cs index b18ce8ee..b34fb48b 100644 --- a/Kyoo.Common/Models/Attributes/PermissionAttribute.cs +++ b/Kyoo.Common/Models/Attributes/PermissionAttribute.cs @@ -24,7 +24,11 @@ namespace Kyoo.Models.Permissions /// /// The needed permission as string. /// - private readonly string _permission; + public string Type { get; } + /// + /// The needed permission kind. + /// + public Kind Kind { get; } /// /// Ask a permission to run an action. @@ -38,7 +42,8 @@ namespace Kyoo.Models.Permissions { if (type.EndsWith("API", StringComparison.OrdinalIgnoreCase)) type = type[..^3]; - _permission = $"{type.ToLower()}.{permission.ToString().ToLower()}"; + Type = type.ToLower(); + Kind = permission; } /// @@ -56,7 +61,7 @@ namespace Kyoo.Models.Permissions /// The string representation. public string AsPermissionString() { - return _permission; + return Type; } } diff --git a/Kyoo/Controllers/PassthroughPermissionValidator.cs b/Kyoo/Controllers/PassthroughPermissionValidator.cs new file mode 100644 index 00000000..d6d2f334 --- /dev/null +++ b/Kyoo/Controllers/PassthroughPermissionValidator.cs @@ -0,0 +1,35 @@ +using Kyoo.Models.Permissions; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; + +namespace Kyoo.Controllers +{ + /// + /// A permission validator that always validate permissions. This effectively disable the permission system. + /// + public class PassthroughPermissionValidator : IPermissionValidator + { + // ReSharper disable once SuggestBaseTypeForParameter + public PassthroughPermissionValidator(ILogger logger) + { + logger.LogWarning("No permission validator has been enabled, all users will have all permissions"); + } + + /// + public IFilterMetadata Create(PermissionAttribute attribute) + { + return new PassthroughValidator(); + } + + /// + public IFilterMetadata Create(PartialPermissionAttribute attribute) + { + return new PassthroughValidator(); + } + + /// + /// An useless filter that does nothing. + /// + private class PassthroughValidator : IFilterMetadata { } + } +} \ No newline at end of file diff --git a/Kyoo/CoreModule.cs b/Kyoo/CoreModule.cs index c2d5e321..3111177e 100644 --- a/Kyoo/CoreModule.cs +++ b/Kyoo/CoreModule.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using Kyoo.Controllers; +using Kyoo.Models.Permissions; using Kyoo.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -93,6 +95,9 @@ namespace Kyoo } services.AddTask(); + + if (services.All(x => x.ServiceType != typeof(IPermissionValidator))) + services.AddSingleton(); } } } \ No newline at end of file diff --git a/Kyoo/Kyoo.csproj b/Kyoo/Kyoo.csproj index 7c39d8d3..5804420f 100644 --- a/Kyoo/Kyoo.csproj +++ b/Kyoo/Kyoo.csproj @@ -45,7 +45,7 @@ - + all diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index a1bda566..062f62f5 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -1,13 +1,11 @@ using System; using System.IO; -using Kyoo.Authentication; using Kyoo.Controllers; using Kyoo.Models; 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; @@ -48,8 +46,7 @@ namespace Kyoo _configuration = configuration; _plugins = new PluginManager(hostProvider, _configuration, loggerFactory.CreateLogger()); - _plugins.LoadPlugins(new IPlugin[] {new CoreModule(), new PostgresModule(configuration, host), - new AuthenticationModule(configuration, loggerFactory, host)}); + _plugins.LoadPlugins(new IPlugin[] {new CoreModule(), new PostgresModule(configuration, host)}); } /// diff --git a/Kyoo/settings.json b/Kyoo/settings.json index 60213e60..4e575d0b 100644 --- a/Kyoo/settings.json +++ b/Kyoo/settings.json @@ -32,8 +32,8 @@ "password": "passphrase" }, "permissions": { - "default": ["read", "play", "write", "admin"], - "newUser": ["read", "play", "write", "admin", "task.read"] + "default": ["overall.read", "overall.write", "overall.create", "overall.delete"], + "newUser": ["overall.read", "overall.write", "overall.create", "overall.delete"] }, "profilePicturePath": "users/", "clients": []