diff --git a/server/RdtClient.Data/Enums/AuthenticationType.cs b/server/RdtClient.Data/Enums/AuthenticationType.cs new file mode 100644 index 0000000..e2f03f5 --- /dev/null +++ b/server/RdtClient.Data/Enums/AuthenticationType.cs @@ -0,0 +1,12 @@ +using System.ComponentModel; + +namespace RdtClient.Data.Enums; + +public enum AuthenticationType +{ + [Description("Username + Password")] + UserNamePassword, + + [Description("No Authentication")] + None +} \ No newline at end of file diff --git a/server/RdtClient.Data/Models/Internal/DbSettings.cs b/server/RdtClient.Data/Models/Internal/DbSettings.cs index 0bc712c..604897a 100644 --- a/server/RdtClient.Data/Models/Internal/DbSettings.cs +++ b/server/RdtClient.Data/Models/Internal/DbSettings.cs @@ -66,6 +66,10 @@ Supports the following parameters: %Z: Torrent size (bytes) %I: Info hash")] public String? RunOnTorrentCompleteArguments { get; set; } = null; + + [DisplayName("Authentication Type")] + [Description("How to authenticate with the client. WARNING: when set to None anyone with access to the URL can use the client without any credentials.")] + public AuthenticationType AuthenticationType { get; set; } = AuthenticationType.UserNamePassword; } public class DbSettingsDownloadClient diff --git a/server/RdtClient.Service/DiConfig.cs b/server/RdtClient.Service/DiConfig.cs index fc66f51..5233fcc 100644 --- a/server/RdtClient.Service/DiConfig.cs +++ b/server/RdtClient.Service/DiConfig.cs @@ -1,5 +1,7 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; using RdtClient.Service.BackgroundServices; +using RdtClient.Service.Middleware; using RdtClient.Service.Services; using RdtClient.Service.Services.TorrentClients; @@ -18,6 +20,8 @@ public static class DiConfig services.AddScoped(); services.AddScoped(); services.AddScoped(); + + services.AddSingleton(); services.AddHostedService(); services.AddHostedService(); diff --git a/server/RdtClient.Service/Middleware/AuthSettingHandler.cs b/server/RdtClient.Service/Middleware/AuthSettingHandler.cs new file mode 100644 index 0000000..e3a552a --- /dev/null +++ b/server/RdtClient.Service/Middleware/AuthSettingHandler.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Authorization; +using RdtClient.Data.Enums; +using RdtClient.Service.Services; + +namespace RdtClient.Service.Middleware; + +public class AuthSettingRequirement : IAuthorizationRequirement +{ + +} + +public class AuthSettingHandler : AuthorizationHandler +{ + protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthSettingRequirement requirement) + { + if (context.User.Identity?.IsAuthenticated == true) + { + context.Succeed(requirement); + } + + if (Settings.Get.General.AuthenticationType == AuthenticationType.None) + { + context.Succeed(requirement); + } + + return Task.CompletedTask; + } +} diff --git a/server/RdtClient.Web/Controllers/AuthController.cs b/server/RdtClient.Web/Controllers/AuthController.cs index 875bb79..531d833 100644 --- a/server/RdtClient.Web/Controllers/AuthController.cs +++ b/server/RdtClient.Web/Controllers/AuthController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using RdtClient.Data.Enums; using RdtClient.Service.Services; namespace RdtClient.Web.Controllers; @@ -21,6 +22,11 @@ public class AuthController : Controller [HttpGet] public async Task IsLoggedIn() { + if (Settings.Get.General.AuthenticationType == AuthenticationType.None) + { + return Ok(); + } + if (User.Identity?.IsAuthenticated == false) { var user = await _authentication.GetUser(); diff --git a/server/RdtClient.Web/Controllers/QBittorrentController.cs b/server/RdtClient.Web/Controllers/QBittorrentController.cs index e9775c6..3a22c95 100644 --- a/server/RdtClient.Web/Controllers/QBittorrentController.cs +++ b/server/RdtClient.Web/Controllers/QBittorrentController.cs @@ -49,7 +49,7 @@ public class QBittorrentController : Controller return await AuthLogin(request); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("auth/logout")] [HttpGet] [HttpPost] @@ -92,7 +92,7 @@ public class QBittorrentController : Controller return Ok(result); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("app/shutdown")] [HttpGet] [HttpPost] @@ -111,7 +111,7 @@ public class QBittorrentController : Controller return Ok(result); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("app/setPreferences")] [HttpGet] [HttpPost] @@ -120,7 +120,7 @@ public class QBittorrentController : Controller return Ok(); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("app/defaultSavePath")] [HttpGet] [HttpPost] @@ -130,7 +130,7 @@ public class QBittorrentController : Controller return Ok(result); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/info")] [HttpGet] [HttpPost] @@ -146,7 +146,7 @@ public class QBittorrentController : Controller return Ok(results); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/info")] [HttpPost] public async Task>> TorrentsFilesPost([FromForm] QBTorrentsInfoRequest request) @@ -154,7 +154,7 @@ public class QBittorrentController : Controller return await TorrentsInfo(request); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/files")] [HttpGet] public async Task>> TorrentsFiles([FromQuery] QBTorrentsHashRequest request) @@ -174,7 +174,7 @@ public class QBittorrentController : Controller return Ok(result); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/files")] [HttpPost] public async Task>> TorrentsFilesPost([FromForm] QBTorrentsHashRequest request) @@ -182,7 +182,7 @@ public class QBittorrentController : Controller return await TorrentsFiles(request); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/properties")] [HttpGet] public async Task>> TorrentsProperties([FromQuery] QBTorrentsHashRequest request) @@ -202,7 +202,7 @@ public class QBittorrentController : Controller return Ok(result); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/properties")] [HttpPost] public async Task>> TorrentsPropertiesPost([FromForm] QBTorrentsHashRequest request) @@ -210,7 +210,7 @@ public class QBittorrentController : Controller return await TorrentsProperties(request); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/pause")] [HttpGet] public async Task TorrentsPause([FromQuery] QBTorrentsHashesRequest request) @@ -230,7 +230,7 @@ public class QBittorrentController : Controller return Ok(); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/topPrio")] [HttpPost] public async Task TorrentsPausePost([FromForm] QBTorrentsHashesRequest request) @@ -238,7 +238,7 @@ public class QBittorrentController : Controller return await TorrentsPause(request); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/resume")] [HttpGet] public async Task TorrentsResume([FromQuery] QBTorrentsHashesRequest request) @@ -258,7 +258,7 @@ public class QBittorrentController : Controller return Ok(); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/topPrio")] [HttpPost] public async Task TorrentsResumePost([FromForm] QBTorrentsHashesRequest request) @@ -266,7 +266,7 @@ public class QBittorrentController : Controller return await TorrentsResume(request); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/setShareLimits")] [HttpGet] [HttpPost] @@ -275,7 +275,7 @@ public class QBittorrentController : Controller return Ok(); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/delete")] [HttpGet] public async Task TorrentsDelete([FromQuery] QBTorrentsDeleteRequest request) @@ -295,7 +295,7 @@ public class QBittorrentController : Controller return Ok(); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/delete")] [HttpPost] public async Task TorrentsDeletePost([FromForm] QBTorrentsDeleteRequest request) @@ -303,7 +303,7 @@ public class QBittorrentController : Controller return await TorrentsDelete(request); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/add")] [HttpGet] public async Task TorrentsAdd([FromQuery] QBTorrentsAddRequest request) @@ -336,7 +336,7 @@ public class QBittorrentController : Controller return Ok(); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/add")] [HttpPost] public async Task TorrentsAddPost([FromForm] QBTorrentsAddRequest request) @@ -362,7 +362,7 @@ public class QBittorrentController : Controller return Ok(); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/setCategory")] [HttpGet] public async Task TorrentsSetCategory([FromQuery] QBTorrentsSetCategoryRequest request) @@ -382,7 +382,7 @@ public class QBittorrentController : Controller return Ok(); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/setCategory")] [HttpPost] public async Task TorrentsSetCategoryPost([FromForm] QBTorrentsSetCategoryRequest request) @@ -390,7 +390,7 @@ public class QBittorrentController : Controller return await TorrentsSetCategory(request); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/categories")] [HttpGet] [HttpPost] @@ -401,7 +401,7 @@ public class QBittorrentController : Controller return Ok(categories); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/createCategory")] [HttpGet] [HttpPost] @@ -417,7 +417,7 @@ public class QBittorrentController : Controller return Ok(); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/removeCategories")] [HttpGet] [HttpPost] @@ -438,7 +438,7 @@ public class QBittorrentController : Controller return Ok(); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/setForcestart")] [HttpGet] [HttpPost] @@ -447,7 +447,7 @@ public class QBittorrentController : Controller return Ok(); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/topPrio")] [HttpGet] public async Task TorrentsTopPrio([FromQuery] QBTorrentsHashesRequest request) @@ -467,7 +467,7 @@ public class QBittorrentController : Controller return Ok(); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("torrents/topPrio")] [HttpPost] public async Task TorrentsTopPrioPost([FromForm] QBTorrentsHashesRequest request) @@ -475,7 +475,7 @@ public class QBittorrentController : Controller return await TorrentsTopPrio(request); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("sync/maindata")] [HttpGet] public async Task SyncMainData() @@ -485,7 +485,7 @@ public class QBittorrentController : Controller return Ok(result); } - [Authorize] + [Authorize(Policy = "AuthSetting")] [Route("sync/maindata")] [HttpPost] public async Task SyncMainDataPost() diff --git a/server/RdtClient.Web/Controllers/SettingsController.cs b/server/RdtClient.Web/Controllers/SettingsController.cs index aa65384..36469c0 100644 --- a/server/RdtClient.Web/Controllers/SettingsController.cs +++ b/server/RdtClient.Web/Controllers/SettingsController.cs @@ -11,7 +11,7 @@ using RdtClient.Service.Services.Downloaders; namespace RdtClient.Web.Controllers; -[Authorize] +[Authorize(Policy = "AuthSetting")] [Route("Api/Settings")] public class SettingsController : Controller { diff --git a/server/RdtClient.Web/Controllers/TorrentsController.cs b/server/RdtClient.Web/Controllers/TorrentsController.cs index 6d26663..88c3fef 100644 --- a/server/RdtClient.Web/Controllers/TorrentsController.cs +++ b/server/RdtClient.Web/Controllers/TorrentsController.cs @@ -7,7 +7,7 @@ using Torrent = RdtClient.Data.Models.Data.Torrent; namespace RdtClient.Web.Controllers; -[Authorize] +[Authorize(Policy = "AuthSetting")] [Route("Api/Torrents")] public class TorrentsController : Controller { diff --git a/server/RdtClient.Web/Program.cs b/server/RdtClient.Web/Program.cs index ecf2247..42adfea 100644 --- a/server/RdtClient.Web/Program.cs +++ b/server/RdtClient.Web/Program.cs @@ -68,7 +68,15 @@ builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationSc options.SlidingExpiration = true; }); -builder.Services.AddAuthorization(); +builder.Services.AddAuthorization( options => +{ + options.AddPolicy("AuthSetting", + policyCorrectUser => + { + policyCorrectUser.Requirements.Add(new AuthSettingRequirement()); + }); +}); + builder.Services.AddIdentity(options => {