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