diff --git a/back/src/Kyoo.Authentication/Controllers/PermissionValidator.cs b/back/src/Kyoo.Authentication/Controllers/PermissionValidator.cs index 77aacbfc..37385010 100644 --- a/back/src/Kyoo.Authentication/Controllers/PermissionValidator.cs +++ b/back/src/Kyoo.Authentication/Controllers/PermissionValidator.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models.Permissions; @@ -30,6 +31,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; namespace Kyoo.Authentication { @@ -169,22 +171,48 @@ namespace Kyoo.Authentication string permStr = $"{permission.ToLower()}.{kind.ToString()!.ToLower()}"; string overallStr = $"{_group.ToString().ToLower()}.{kind.ToString()!.ToLower()}"; - AuthenticateResult res = await context.HttpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme); + AuthenticateResult res = _ApiKeyCheck(context); + if (res.None) + res = await context.HttpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme); if (res.Succeeded) { ICollection permissions = res.Principal.GetPermissions(); if (permissions.All(x => x != permStr && x != overallStr)) context.Result = _ErrorResult($"Missing permission {permStr} or {overallStr}", StatusCodes.Status403Forbidden); } - else + else if (res.None) { ICollection permissions = _options.CurrentValue.Default ?? Array.Empty(); - if (res.Failure != null || permissions.All(x => x != permStr && x != overallStr)) + if (permissions.All(x => x != permStr && x != overallStr)) { - context.Result = _ErrorResult("Token non present or invalid (it may have expired). " + - $"Unlogged user does not have permission {permStr} or {overallStr}", StatusCodes.Status401Unauthorized); + context.Result = _ErrorResult($"Unlogged user does not have permission {permStr} or {overallStr}", StatusCodes.Status401Unauthorized); } } + else if (res.Failure != null) + context.Result = _ErrorResult(res.Failure.Message, StatusCodes.Status403Forbidden); + } + + private AuthenticateResult _ApiKeyCheck(ActionContext context) + { + if (!context.HttpContext.Request.Headers.TryGetValue("X-API-Key", out StringValues apiKey)) + return AuthenticateResult.NoResult(); + if (!_options.CurrentValue.ApiKeys.Contains(apiKey)) + return AuthenticateResult.Fail("Invalid API-Key."); + return AuthenticateResult.Success( + new AuthenticationTicket( + new ClaimsPrincipal( + new[] + { + new ClaimsIdentity(new[] + { + // TODO: Make permission configurable, for now every APIKEY as all permissions. + new Claim(Claims.Permissions, string.Join(',', PermissionOption.Admin)) + }) + } + ), + "apikey" + ) + ); } } diff --git a/back/src/Kyoo.Authentication/Views/AuthApi.cs b/back/src/Kyoo.Authentication/Views/AuthApi.cs index a698ce37..f42d0ed5 100644 --- a/back/src/Kyoo.Authentication/Views/AuthApi.cs +++ b/back/src/Kyoo.Authentication/Views/AuthApi.cs @@ -96,7 +96,7 @@ namespace Kyoo.Authentication.Views [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(RequestError))] public async Task> Login([FromBody] LoginRequest request) { - User user = await _users.GetOrDefault(x => x.Username == request.Username); + User? user = await _users.GetOrDefault(x => x.Username == request.Username); if (user == null || !BCryptNet.Verify(request.Password, user.Password)) return Forbid(new RequestError("The user and password does not match."));