diff --git a/Kyoo.Authentication/AuthManager.cs b/Kyoo.Authentication/AuthManager.cs deleted file mode 100644 index 2002db63..00000000 --- a/Kyoo.Authentication/AuthManager.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using IdentityServer4.Extensions; -using Microsoft.AspNetCore.Authorization; -using Microsoft.Extensions.Configuration; - -namespace Kyoo.Authentication -{ - public class AuthorizationValidatorHandler : AuthorizationHandler - { - private readonly IConfiguration _configuration; - - public AuthorizationValidatorHandler(IConfiguration configuration) - { - _configuration = configuration; - } - - protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthorizationValidator requirement) - { - if (!context.User.IsAuthenticated()) - { - string defaultPerms = _configuration.GetValue("defaultPermissions"); - if (defaultPerms.Split(',').Contains(requirement.Permission.ToLower())) - context.Succeed(requirement); - } - else - { - Claim perms = context.User.Claims.FirstOrDefault(x => x.Type == "permissions"); - if (perms != null && perms.Value.Split(",").Contains(requirement.Permission.ToLower())) - context.Succeed(requirement); - } - - return Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/Kyoo.Authentication/AuthRequirement.cs b/Kyoo.Authentication/AuthRequirement.cs deleted file mode 100644 index e27e5d65..00000000 --- a/Kyoo.Authentication/AuthRequirement.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.AspNetCore.Authorization; - -namespace Kyoo.Authentication -{ - public class AuthorizationValidator : IAuthorizationRequirement - { - public string Permission { get; } - - public AuthorizationValidator(string permission) - { - Permission = permission; - } - } -} \ No newline at end of file diff --git a/Kyoo.Authentication/AuthenticationModule.cs b/Kyoo.Authentication/AuthenticationModule.cs index 969f375d..e49cee9b 100644 --- a/Kyoo.Authentication/AuthenticationModule.cs +++ b/Kyoo.Authentication/AuthenticationModule.cs @@ -79,8 +79,10 @@ namespace Kyoo.Authentication // .AddDefaultTokenProviders() // .AddEntityFrameworkStores(); + services.Configure(_configuration.GetSection(PermissionOption.Path)); CertificateOption certificateOptions = new(); _configuration.GetSection(CertificateOption.Path).Bind(certificateOptions); + services.AddIdentityServer(options => { options.IssuerUri = publicUrl; @@ -136,7 +138,7 @@ namespace Kyoo.Authentication { policy.AuthenticationSchemes.Add(IdentityConstants.ApplicationScheme); policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme); - policy.AddRequirements(new AuthorizationValidator(permission)); + policy.AddRequirements(new AuthRequirement(permission)); policy.RequireScope($"kyoo.{permission.ToLower()}"); }); } @@ -153,8 +155,6 @@ namespace Kyoo.Authentication /// public void ConfigureAspNet(IApplicationBuilder app) { - app.UseAuthorization(); - app.UseCookiePolicy(new CookiePolicyOptions { MinimumSameSitePolicy = SameSiteMode.Strict @@ -166,6 +166,7 @@ namespace Kyoo.Authentication return next(); }); app.UseIdentityServer(); + app.UseAuthorization(); } } } \ No newline at end of file diff --git a/Kyoo.Authentication/Controllers/AuthorizationValidatorHandler.cs b/Kyoo.Authentication/Controllers/AuthorizationValidatorHandler.cs new file mode 100644 index 00000000..00df30db --- /dev/null +++ b/Kyoo.Authentication/Controllers/AuthorizationValidatorHandler.cs @@ -0,0 +1,51 @@ +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())) + context.Succeed(requirement); + } + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Kyoo.Authentication/Models/AuthRequirement.cs b/Kyoo.Authentication/Models/AuthRequirement.cs new file mode 100644 index 00000000..b74818b5 --- /dev/null +++ b/Kyoo.Authentication/Models/AuthRequirement.cs @@ -0,0 +1,24 @@ +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/Models/PermissionOption.cs b/Kyoo.Authentication/Models/PermissionOption.cs new file mode 100644 index 00000000..772f3fb5 --- /dev/null +++ b/Kyoo.Authentication/Models/PermissionOption.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace Kyoo.Authentication.Models +{ + /// + /// Permission options. + /// + public class PermissionOption + { + /// + /// The path to get this option from the root configuration. + /// + public const string Path = "authentication:permissions"; + + /// + /// The default permissions that will be given to a non-connected user. + /// + public ICollection Default { get; set; } + + /// + /// Permissions applied to a new user. + /// + public ICollection NewUser { get; set; } + } +} \ No newline at end of file diff --git a/Kyoo.Authentication/AccountApi.cs b/Kyoo.Authentication/Views/AccountApi.cs similarity index 87% rename from Kyoo.Authentication/AccountApi.cs rename to Kyoo.Authentication/Views/AccountApi.cs index dc02fcda..7acf5226 100644 --- a/Kyoo.Authentication/AccountApi.cs +++ b/Kyoo.Authentication/Views/AccountApi.cs @@ -12,7 +12,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.Configuration; using SignInResult = Microsoft.AspNetCore.Identity.SignInResult; @@ -47,29 +46,8 @@ namespace Kyoo.Api [FromForm(Name = "picture")] public IFormFile Picture { get; set; } } - - [ApiController] - public class AccountUiController : Controller - { - [HttpGet("login")] - public IActionResult Index() - { - return new PhysicalFileResult(Path.GetFullPath("login/login.html"), "text/html"); - } - - [HttpGet("login/{*file}")] - public IActionResult Index(string file) - { - string path = Path.Combine(Path.GetFullPath("login/"), file); - if (!System.IO.File.Exists(path)) - return NotFound(); - FileExtensionContentTypeProvider provider = new FileExtensionContentTypeProvider(); - if (!provider.TryGetContentType(path, out string contentType)) - contentType = "text/plain"; - return new PhysicalFileResult(path, contentType); - } - } - + + [Route("api/[controller]")] [ApiController] public class AccountController : Controller, IProfileService @@ -100,7 +78,7 @@ namespace Kyoo.Api return BadRequest(new[] {new {code = "username", description = "Username must be at least 4 characters."}}); if (!new EmailAddressAttribute().IsValid(user.Email)) return BadRequest(new[] {new {code = "email", description = "Email must be valid."}}); - User account = new User {UserName = user.Username, Email = user.Email}; + User account = new() {UserName = user.Username, Email = user.Email}; IdentityResult result = await _userManager.CreateAsync(account, user.Password); if (!result.Succeeded) return BadRequest(result.Errors); @@ -151,7 +129,7 @@ namespace Kyoo.Api User user = await _userManager.GetUserAsync(context.Subject); if (user != null) { - List claims = new List + List claims = new() { new Claim("email", user.Email), new Claim("username", user.UserName), diff --git a/Kyoo.WebLogin/login.html b/Kyoo.WebLogin/index.html similarity index 91% rename from Kyoo.WebLogin/login.html rename to Kyoo.WebLogin/index.html index 0c32da3d..e875aab5 100644 --- a/Kyoo.WebLogin/login.html +++ b/Kyoo.WebLogin/index.html @@ -3,11 +3,11 @@ Kyoo - Login - - - - - + + + + +
@@ -85,6 +85,6 @@
- + - \ No newline at end of file + diff --git a/Kyoo/Kyoo.csproj b/Kyoo/Kyoo.csproj index d4e376b4..5804420f 100644 --- a/Kyoo/Kyoo.csproj +++ b/Kyoo/Kyoo.csproj @@ -40,7 +40,13 @@ - + + + + + + all + @@ -74,7 +80,7 @@ true - login/%(LoginFiles.RecursiveDir)%(LoginFiles.Filename)%(LoginFiles.Extension) + wwwroot/login/%(LoginFiles.RecursiveDir)%(LoginFiles.Filename)%(LoginFiles.Extension) PreserveNewest true @@ -87,7 +93,7 @@ - + diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index b60b3e09..c172fd67 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -76,16 +76,6 @@ namespace Kyoo }); services.AddHttpClient(); - // services.AddAuthorization(options => - // { - // string[] permissions = {"Read", "Write", "Play", "Admin"}; - // foreach (string permission in permissions) - // options.AddPolicy(permission, policy => - // { - // policy.AddRequirements(new AuthorizationValidator(permission)); - // }); - // }); - // services.AddAuthentication() services.AddTransient(typeof(Lazy<>), typeof(LazyDi<>)); services.AddSingleton(_plugins); @@ -110,6 +100,7 @@ namespace Kyoo FileExtensionContentTypeProvider contentTypeProvider = new(); contentTypeProvider.Mappings[".data"] = "application/octet-stream"; + app.UseDefaultFiles(); app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = contentTypeProvider, @@ -119,7 +110,6 @@ namespace Kyoo app.UseSpaStaticFiles(); app.UseRouting(); - // app.UseAuthorization(); app.Use((ctx, next) => { @@ -135,13 +125,13 @@ namespace Kyoo }); app.UseResponseCompression(); - // app.UseSpa(spa => - // { - // spa.Options.SourcePath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Kyoo.WebApp"); - // - // if (env.IsDevelopment()) - // spa.UseAngularCliServer("start"); - // }); + app.UseSpa(spa => + { + spa.Options.SourcePath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Kyoo.WebApp"); + + if (env.IsDevelopment()) + spa.UseAngularCliServer("start"); + }); _plugins.ConfigureAspnet(app); diff --git a/Kyoo/settings.json b/Kyoo/settings.json index 8cd453e2..246b84de 100644 --- a/Kyoo/settings.json +++ b/Kyoo/settings.json @@ -29,6 +29,10 @@ "file": "certificate.pfx", "oldFile": "oldCertificate.pfx", "password": "passphrase" + }, + "permissions": { + "default": ["read", "play", "write", "admin"], + "newUser": ["read", "play", "write", "admin"] } }, @@ -45,8 +49,6 @@ "providerPath": "providers", "profilePicturePath": "users/", "plugins": "plugins/", - "defaultPermissions": "read,play,write,admin", - "newUserPermissions": "read,play,write,admin", "regex": "(?:\\/(?.*?))?\\/(?.*?)(?: \\(\\d+\\))?\\/\\k(?: \\(\\d+\\))?(?:(?: S(?\\d+)E(?\\d+))| (?\\d+))?.*$", "subtitleRegex": "^(?.*)\\.(?\\w{1,3})\\.(?default\\.)?(?forced\\.)?.*$" }