diff --git a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs index bb6192b03d..a753d60838 100644 --- a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs +++ b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs @@ -1,6 +1,7 @@ using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; +using Jellyfin.Api.Enums; using MediaBrowser.Controller.Net; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; @@ -8,10 +9,21 @@ using Microsoft.Extensions.Options; namespace Jellyfin.Api.Auth { + /// + /// Custom authentication handler wrapping the legacy authentication. + /// public class CustomAuthenticationHandler : AuthenticationHandler { private readonly IAuthService _authService; + /// + /// Initializes a new instance of the class. + /// + /// The jellyfin authentication service. + /// Options monitor. + /// The logger. + /// The url encoder. + /// The system clock. public CustomAuthenticationHandler( IAuthService authService, IOptionsMonitor options, @@ -22,6 +34,7 @@ namespace Jellyfin.Api.Auth _authService = authService; } + /// protected override Task HandleAuthenticateAsync() { var authenticatedAttribute = new AuthenticatedAttribute(); @@ -36,7 +49,9 @@ namespace Jellyfin.Api.Auth var claims = new[] { new Claim(ClaimTypes.Name, user.Name), - new Claim(ClaimTypes.Role, user.Policy.IsAdministrator ? "Administrator" : "User"), + new Claim( + ClaimTypes.Role, + value: user.Policy.IsAdministrator ? UserRole.Administrator.ToString() : UserRole.User.ToString()) }; var identity = new ClaimsIdentity(claims, Scheme.Name); var principal = new ClaimsPrincipal(identity); diff --git a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs index 73925cd616..f07e568dea 100644 --- a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs +++ b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandler.cs @@ -1,27 +1,35 @@ using System.Threading.Tasks; +using Jellyfin.Api.Enums; using MediaBrowser.Common.Configuration; using Microsoft.AspNetCore.Authorization; namespace Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy { + /// + /// Authorization handler for requiring first time setup or elevated privileges. + /// public class FirstTimeSetupOrElevatedHandler : AuthorizationHandler { private readonly IConfigurationManager _configurationManager; + /// + /// Initializes a new instance of the class. + /// + /// The jellyfin configuration manager. public FirstTimeSetupOrElevatedHandler(IConfigurationManager configurationManager) { _configurationManager = configurationManager; } - protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrElevatedRequirement firstTimeSetupOrElevatedRequirement) + /// + protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrElevatedRequirement firstTimeSetupOrElevatedRequirement) { if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted) { context.Succeed(firstTimeSetupOrElevatedRequirement); } - else if (context.User.IsInRole("Administrator")) + else if (context.User.IsInRole(UserRole.Administrator.ToString())) { - // TODO user role enum context.Succeed(firstTimeSetupOrElevatedRequirement); } else diff --git a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedRequirement.cs b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedRequirement.cs index 42436c870d..a590155420 100644 --- a/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedRequirement.cs +++ b/Jellyfin.Api/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedRequirement.cs @@ -2,6 +2,9 @@ using Microsoft.AspNetCore.Authorization; namespace Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy { + /// + /// The authorization requirement, requiring first time setup or elevated privileges, for the authorization handler. + /// public class FirstTimeSetupOrElevatedRequirement : IAuthorizationRequirement { } diff --git a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs b/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs index 6948274582..8674f3e262 100644 --- a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs +++ b/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationHandler.cs @@ -1,13 +1,18 @@ using System.Threading.Tasks; +using Jellyfin.Api.Enums; using Microsoft.AspNetCore.Authorization; namespace Jellyfin.Api.Auth.RequiresElevationPolicy { + /// + /// Authorization handler for requiring elevated privileges. + /// public class RequiresElevationHandler : AuthorizationHandler { + /// protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RequiresElevationRequirement requirement) { - if (context.User.IsInRole("Administrator")) + if (context.User.IsInRole(UserRole.Administrator.ToString())) { context.Succeed(requirement); } diff --git a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationRequirement.cs b/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationRequirement.cs index dd51cd3c20..cfff1cc0c5 100644 --- a/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationRequirement.cs +++ b/Jellyfin.Api/Auth/RequiresElevationPolicy/RequiresElevationRequirement.cs @@ -2,8 +2,10 @@ using Microsoft.AspNetCore.Authorization; namespace Jellyfin.Api.Auth.RequiresElevationPolicy { + /// + /// The authorization requirement for requiring elevated privileges in the authorization handler. + /// public class RequiresElevationRequirement : IAuthorizationRequirement { - } } diff --git a/Jellyfin.Api/BaseJellyfinApiController.cs b/Jellyfin.Api/BaseJellyfinApiController.cs index 796a8039af..1f4508e6cb 100644 --- a/Jellyfin.Api/BaseJellyfinApiController.cs +++ b/Jellyfin.Api/BaseJellyfinApiController.cs @@ -2,10 +2,12 @@ using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Api { + /// + /// Base api controller for the API setting a default route. + /// [ApiController] [Route("[controller]")] public class BaseJellyfinApiController : ControllerBase { - } } diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index 271745e057..0e7d17a27f 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -8,18 +8,29 @@ using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Api.Controllers { + /// + /// The startup wizard controller. + /// [Authorize(Policy = "FirstTimeSetupOrElevated")] public class StartupController : BaseJellyfinApiController { private readonly IServerConfigurationManager _config; private readonly IUserManager _userManager; + /// + /// Initializes a new instance of the class. + /// + /// The server configuration manager. + /// The user manager. public StartupController(IServerConfigurationManager config, IUserManager userManager) { _config = config; _userManager = userManager; } + /// + /// Api endpoint for completing the startup wizard. + /// [HttpPost("Complete")] public void CompleteWizard() { @@ -28,6 +39,10 @@ namespace Jellyfin.Api.Controllers _config.SaveConfiguration(); } + /// + /// Endpoint for getting the initial startup wizard configuration. + /// + /// The initial startup wizard configuration. [HttpGet("Configuration")] public StartupConfigurationDto GetStartupConfiguration() { @@ -41,6 +56,12 @@ namespace Jellyfin.Api.Controllers return result; } + /// + /// Endpoint for updating the initial startup wizard configuration. + /// + /// The UI language culture. + /// The metadata country code. + /// The preferred language for metadata. [HttpPost("Configuration")] public void UpdateInitialConfiguration( [FromForm] string uiCulture, @@ -53,6 +74,11 @@ namespace Jellyfin.Api.Controllers _config.SaveConfiguration(); } + /// + /// Endpoint for (dis)allowing remote access and UPnP. + /// + /// Enable remote access. + /// Enable UPnP. [HttpPost("RemoteAccess")] public void SetRemoteAccess([FromForm] bool enableRemoteAccess, [FromForm] bool enableAutomaticPortMapping) { @@ -61,8 +87,12 @@ namespace Jellyfin.Api.Controllers _config.SaveConfiguration(); } + /// + /// Endpoint for returning the first user. + /// + /// The first user. [HttpGet("User")] - public StartupUserDto GetUser() + public StartupUserDto GetFirstUser() { var user = _userManager.Users.First(); @@ -73,6 +103,11 @@ namespace Jellyfin.Api.Controllers }; } + /// + /// Endpoint for updating the user name and password. + /// + /// The DTO containing username and password. + /// The async task. [HttpPost("User")] public async Task UpdateUser([FromForm] StartupUserDto startupUserDto) { diff --git a/Jellyfin.Api/Enums/UserRole.cs b/Jellyfin.Api/Enums/UserRole.cs new file mode 100644 index 0000000000..05826d9f41 --- /dev/null +++ b/Jellyfin.Api/Enums/UserRole.cs @@ -0,0 +1,23 @@ +namespace Jellyfin.Api.Enums +{ + /// + /// Enum for user roles used in the authentication and authorization for the API. + /// + public enum UserRole + { + /// + /// Guest user. + /// + Guest = 0, + + /// + /// Regular user with no special privileges. + /// + User = 1, + + /// + /// Administrator user with elevated privileges. + /// + Administrator = 2 + } +} diff --git a/Jellyfin.Api/Extensions/ApiApplicationBuilderExtensions.cs b/Jellyfin.Api/Extensions/ApiApplicationBuilderExtensions.cs index 18442bf272..f70466ebec 100644 --- a/Jellyfin.Api/Extensions/ApiApplicationBuilderExtensions.cs +++ b/Jellyfin.Api/Extensions/ApiApplicationBuilderExtensions.cs @@ -2,8 +2,16 @@ using Microsoft.AspNetCore.Builder; namespace Jellyfin.Api.Extensions { + /// + /// Extensions for adding API specific functionality to the application pipeline. + /// public static class ApiApplicationBuilderExtensions { + /// + /// Adds swagger and swagger UI to the application pipeline. + /// + /// The application builder. + /// The updated application builder. public static IApplicationBuilder UseJellyfinApiSwagger(this IApplicationBuilder applicationBuilder) { applicationBuilder.UseSwagger(); diff --git a/Jellyfin.Api/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Api/Extensions/ApiServiceCollectionExtensions.cs index 1c682f8e43..38f5f6d390 100644 --- a/Jellyfin.Api/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Api/Extensions/ApiServiceCollectionExtensions.cs @@ -1,4 +1,3 @@ -using Emby.Server.Implementations; using Jellyfin.Api.Auth; using Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy; using Jellyfin.Api.Auth.RequiresElevationPolicy; @@ -12,8 +11,16 @@ using Microsoft.OpenApi.Models; namespace Jellyfin.Api.Extensions { + /// + /// API specific extensions for the service collection. + /// public static class ApiServiceCollectionExtensions { + /// + /// Adds jellyfin API authorization policies to the DI container. + /// + /// The service collection. + /// The updated service collection. public static IServiceCollection AddJellyfinApiAuthorization(this IServiceCollection serviceCollection) { serviceCollection.AddSingleton(); @@ -37,12 +44,23 @@ namespace Jellyfin.Api.Extensions }); } + /// + /// Adds custom legacy authentication to the service collection. + /// + /// The service collection. + /// The updated service collection. public static AuthenticationBuilder AddCustomAuthentication(this IServiceCollection serviceCollection) { return serviceCollection.AddAuthentication("CustomAuthentication") .AddScheme("CustomAuthentication", null); } + /// + /// Extension method for adding the jellyfin API to the service collection. + /// + /// The service collection. + /// The base url for the API. + /// The MVC builder. public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, string baseUrl) { return serviceCollection.AddMvc(opts => @@ -55,12 +73,18 @@ namespace Jellyfin.Api.Extensions opts.UseGeneralRoutePrefix(baseUrl); }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2) + // Clear app parts to avoid other assemblies being picked up .ConfigureApplicationPartManager(a => a.ApplicationParts.Clear()) .AddApplicationPart(typeof(StartupController).Assembly) .AddControllersAsServices(); } + /// + /// Adds Swagger to the service collection. + /// + /// The service collection. + /// The updated service collection. public static IServiceCollection AddJellyfinApiSwagger(this IServiceCollection serviceCollection) { return serviceCollection.AddSwaggerGen(c => diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index d77861cc4a..1cc23c07b4 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -2,6 +2,8 @@ netstandard2.1 + true + true @@ -15,4 +17,16 @@ + + + + + + + + + + ../jellyfin.ruleset + + diff --git a/Jellyfin.Api/Models/Startup/StartupConfigurationDto.cs b/Jellyfin.Api/Models/Startup/StartupConfigurationDto.cs index 769d2e1bb6..dac15e412c 100644 --- a/Jellyfin.Api/Models/Startup/StartupConfigurationDto.cs +++ b/Jellyfin.Api/Models/Startup/StartupConfigurationDto.cs @@ -1,9 +1,23 @@ namespace Jellyfin.Api.Models.Startup { + /// + /// The startup configuration DTO. + /// public class StartupConfigurationDto { + /// + /// Gets or sets UI language culture. + /// public string UICulture { get; set; } + + /// + /// Gets or sets the metadata country code. + /// public string MetadataCountryCode { get; set; } + + /// + /// Gets or sets the preferred language for the metadata. + /// public string PreferredMetadataLanguage { get; set; } } } diff --git a/Jellyfin.Api/Models/Startup/StartupUserDto.cs b/Jellyfin.Api/Models/Startup/StartupUserDto.cs index c7c2e8cb04..7e890d76a0 100644 --- a/Jellyfin.Api/Models/Startup/StartupUserDto.cs +++ b/Jellyfin.Api/Models/Startup/StartupUserDto.cs @@ -1,8 +1,18 @@ namespace Jellyfin.Api.Models.Startup { + /// + /// The startup user DTO. + /// public class StartupUserDto { + /// + /// Gets or sets the username. + /// public string Name { get; set; } + + /// + /// Gets or sets the user's password. + /// public string Password { get; set; } } } diff --git a/Jellyfin.Api/MvcRoutePrefix.cs b/Jellyfin.Api/MvcRoutePrefix.cs index 974a2a8852..e009730947 100644 --- a/Jellyfin.Api/MvcRoutePrefix.cs +++ b/Jellyfin.Api/MvcRoutePrefix.cs @@ -3,10 +3,18 @@ using System.Linq; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationModels; -namespace Emby.Server.Implementations +namespace Jellyfin.Api { + /// + /// Route prefixing for ASP.NET MVC. + /// public static class MvcRoutePrefix { + /// + /// Adds route prefixes to the MVC conventions. + /// + /// The MVC options. + /// The list of prefixes. public static void UseGeneralRoutePrefix(this MvcOptions opts, params string[] prefixes) { opts.Conventions.Insert(0, new RoutePrefixConvention(prefixes));