Add style rules and fix it all

This commit is contained in:
Claus Vium 2019-11-23 20:31:17 +01:00
parent c9669a0d21
commit c2cdbc909b
14 changed files with 181 additions and 10 deletions

View file

@ -1,6 +1,7 @@
using System.Security.Claims; using System.Security.Claims;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Api.Enums;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -8,10 +9,21 @@ using Microsoft.Extensions.Options;
namespace Jellyfin.Api.Auth namespace Jellyfin.Api.Auth
{ {
/// <summary>
/// Custom authentication handler wrapping the legacy authentication.
/// </summary>
public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{ {
private readonly IAuthService _authService; private readonly IAuthService _authService;
/// <summary>
/// Initializes a new instance of the <see cref="CustomAuthenticationHandler" /> class.
/// </summary>
/// <param name="authService">The jellyfin authentication service.</param>
/// <param name="options">Options monitor.</param>
/// <param name="logger">The logger.</param>
/// <param name="encoder">The url encoder.</param>
/// <param name="clock">The system clock.</param>
public CustomAuthenticationHandler( public CustomAuthenticationHandler(
IAuthService authService, IAuthService authService,
IOptionsMonitor<AuthenticationSchemeOptions> options, IOptionsMonitor<AuthenticationSchemeOptions> options,
@ -22,6 +34,7 @@ namespace Jellyfin.Api.Auth
_authService = authService; _authService = authService;
} }
/// <inheritdoc />
protected override Task<AuthenticateResult> HandleAuthenticateAsync() protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{ {
var authenticatedAttribute = new AuthenticatedAttribute(); var authenticatedAttribute = new AuthenticatedAttribute();
@ -36,7 +49,9 @@ namespace Jellyfin.Api.Auth
var claims = new[] var claims = new[]
{ {
new Claim(ClaimTypes.Name, user.Name), 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 identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity); var principal = new ClaimsPrincipal(identity);

View file

@ -1,27 +1,35 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Api.Enums;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
namespace Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy namespace Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy
{ {
/// <summary>
/// Authorization handler for requiring first time setup or elevated privileges.
/// </summary>
public class FirstTimeSetupOrElevatedHandler : AuthorizationHandler<FirstTimeSetupOrElevatedRequirement> public class FirstTimeSetupOrElevatedHandler : AuthorizationHandler<FirstTimeSetupOrElevatedRequirement>
{ {
private readonly IConfigurationManager _configurationManager; private readonly IConfigurationManager _configurationManager;
/// <summary>
/// Initializes a new instance of the <see cref="FirstTimeSetupOrElevatedHandler" /> class.
/// </summary>
/// <param name="configurationManager">The jellyfin configuration manager.</param>
public FirstTimeSetupOrElevatedHandler(IConfigurationManager configurationManager) public FirstTimeSetupOrElevatedHandler(IConfigurationManager configurationManager)
{ {
_configurationManager = configurationManager; _configurationManager = configurationManager;
} }
/// <inheritdoc />
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrElevatedRequirement firstTimeSetupOrElevatedRequirement) protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrElevatedRequirement firstTimeSetupOrElevatedRequirement)
{ {
if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted) if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted)
{ {
context.Succeed(firstTimeSetupOrElevatedRequirement); context.Succeed(firstTimeSetupOrElevatedRequirement);
} }
else if (context.User.IsInRole("Administrator")) else if (context.User.IsInRole(UserRole.Administrator.ToString()))
{ {
// TODO user role enum
context.Succeed(firstTimeSetupOrElevatedRequirement); context.Succeed(firstTimeSetupOrElevatedRequirement);
} }
else else

View file

@ -2,6 +2,9 @@ using Microsoft.AspNetCore.Authorization;
namespace Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy namespace Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy
{ {
/// <summary>
/// The authorization requirement, requiring first time setup or elevated privileges, for the authorization handler.
/// </summary>
public class FirstTimeSetupOrElevatedRequirement : IAuthorizationRequirement public class FirstTimeSetupOrElevatedRequirement : IAuthorizationRequirement
{ {
} }

View file

@ -1,13 +1,18 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Api.Enums;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
namespace Jellyfin.Api.Auth.RequiresElevationPolicy namespace Jellyfin.Api.Auth.RequiresElevationPolicy
{ {
/// <summary>
/// Authorization handler for requiring elevated privileges.
/// </summary>
public class RequiresElevationHandler : AuthorizationHandler<RequiresElevationRequirement> public class RequiresElevationHandler : AuthorizationHandler<RequiresElevationRequirement>
{ {
/// <inheritdoc />
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RequiresElevationRequirement requirement) protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RequiresElevationRequirement requirement)
{ {
if (context.User.IsInRole("Administrator")) if (context.User.IsInRole(UserRole.Administrator.ToString()))
{ {
context.Succeed(requirement); context.Succeed(requirement);
} }

View file

@ -2,8 +2,10 @@ using Microsoft.AspNetCore.Authorization;
namespace Jellyfin.Api.Auth.RequiresElevationPolicy namespace Jellyfin.Api.Auth.RequiresElevationPolicy
{ {
/// <summary>
/// The authorization requirement for requiring elevated privileges in the authorization handler.
/// </summary>
public class RequiresElevationRequirement : IAuthorizationRequirement public class RequiresElevationRequirement : IAuthorizationRequirement
{ {
} }
} }

View file

@ -2,10 +2,12 @@ using Microsoft.AspNetCore.Mvc;
namespace Jellyfin.Api namespace Jellyfin.Api
{ {
/// <summary>
/// Base api controller for the API setting a default route.
/// </summary>
[ApiController] [ApiController]
[Route("[controller]")] [Route("[controller]")]
public class BaseJellyfinApiController : ControllerBase public class BaseJellyfinApiController : ControllerBase
{ {
} }
} }

View file

@ -8,18 +8,29 @@ using Microsoft.AspNetCore.Mvc;
namespace Jellyfin.Api.Controllers namespace Jellyfin.Api.Controllers
{ {
/// <summary>
/// The startup wizard controller.
/// </summary>
[Authorize(Policy = "FirstTimeSetupOrElevated")] [Authorize(Policy = "FirstTimeSetupOrElevated")]
public class StartupController : BaseJellyfinApiController public class StartupController : BaseJellyfinApiController
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
/// <summary>
/// Initializes a new instance of the <see cref="StartupController" /> class.
/// </summary>
/// <param name="config">The server configuration manager.</param>
/// <param name="userManager">The user manager.</param>
public StartupController(IServerConfigurationManager config, IUserManager userManager) public StartupController(IServerConfigurationManager config, IUserManager userManager)
{ {
_config = config; _config = config;
_userManager = userManager; _userManager = userManager;
} }
/// <summary>
/// Api endpoint for completing the startup wizard.
/// </summary>
[HttpPost("Complete")] [HttpPost("Complete")]
public void CompleteWizard() public void CompleteWizard()
{ {
@ -28,6 +39,10 @@ namespace Jellyfin.Api.Controllers
_config.SaveConfiguration(); _config.SaveConfiguration();
} }
/// <summary>
/// Endpoint for getting the initial startup wizard configuration.
/// </summary>
/// <returns>The initial startup wizard configuration.</returns>
[HttpGet("Configuration")] [HttpGet("Configuration")]
public StartupConfigurationDto GetStartupConfiguration() public StartupConfigurationDto GetStartupConfiguration()
{ {
@ -41,6 +56,12 @@ namespace Jellyfin.Api.Controllers
return result; return result;
} }
/// <summary>
/// Endpoint for updating the initial startup wizard configuration.
/// </summary>
/// <param name="uiCulture">The UI language culture.</param>
/// <param name="metadataCountryCode">The metadata country code.</param>
/// <param name="preferredMetadataLanguage">The preferred language for metadata.</param>
[HttpPost("Configuration")] [HttpPost("Configuration")]
public void UpdateInitialConfiguration( public void UpdateInitialConfiguration(
[FromForm] string uiCulture, [FromForm] string uiCulture,
@ -53,6 +74,11 @@ namespace Jellyfin.Api.Controllers
_config.SaveConfiguration(); _config.SaveConfiguration();
} }
/// <summary>
/// Endpoint for (dis)allowing remote access and UPnP.
/// </summary>
/// <param name="enableRemoteAccess">Enable remote access.</param>
/// <param name="enableAutomaticPortMapping">Enable UPnP.</param>
[HttpPost("RemoteAccess")] [HttpPost("RemoteAccess")]
public void SetRemoteAccess([FromForm] bool enableRemoteAccess, [FromForm] bool enableAutomaticPortMapping) public void SetRemoteAccess([FromForm] bool enableRemoteAccess, [FromForm] bool enableAutomaticPortMapping)
{ {
@ -61,8 +87,12 @@ namespace Jellyfin.Api.Controllers
_config.SaveConfiguration(); _config.SaveConfiguration();
} }
/// <summary>
/// Endpoint for returning the first user.
/// </summary>
/// <returns>The first user.</returns>
[HttpGet("User")] [HttpGet("User")]
public StartupUserDto GetUser() public StartupUserDto GetFirstUser()
{ {
var user = _userManager.Users.First(); var user = _userManager.Users.First();
@ -73,6 +103,11 @@ namespace Jellyfin.Api.Controllers
}; };
} }
/// <summary>
/// Endpoint for updating the user name and password.
/// </summary>
/// <param name="startupUserDto">The DTO containing username and password.</param>
/// <returns>The async task.</returns>
[HttpPost("User")] [HttpPost("User")]
public async Task UpdateUser([FromForm] StartupUserDto startupUserDto) public async Task UpdateUser([FromForm] StartupUserDto startupUserDto)
{ {

View file

@ -0,0 +1,23 @@
namespace Jellyfin.Api.Enums
{
/// <summary>
/// Enum for user roles used in the authentication and authorization for the API.
/// </summary>
public enum UserRole
{
/// <summary>
/// Guest user.
/// </summary>
Guest = 0,
/// <summary>
/// Regular user with no special privileges.
/// </summary>
User = 1,
/// <summary>
/// Administrator user with elevated privileges.
/// </summary>
Administrator = 2
}
}

View file

@ -2,8 +2,16 @@ using Microsoft.AspNetCore.Builder;
namespace Jellyfin.Api.Extensions namespace Jellyfin.Api.Extensions
{ {
/// <summary>
/// Extensions for adding API specific functionality to the application pipeline.
/// </summary>
public static class ApiApplicationBuilderExtensions public static class ApiApplicationBuilderExtensions
{ {
/// <summary>
/// Adds swagger and swagger UI to the application pipeline.
/// </summary>
/// <param name="applicationBuilder">The application builder.</param>
/// <returns>The updated application builder.</returns>
public static IApplicationBuilder UseJellyfinApiSwagger(this IApplicationBuilder applicationBuilder) public static IApplicationBuilder UseJellyfinApiSwagger(this IApplicationBuilder applicationBuilder)
{ {
applicationBuilder.UseSwagger(); applicationBuilder.UseSwagger();

View file

@ -1,4 +1,3 @@
using Emby.Server.Implementations;
using Jellyfin.Api.Auth; using Jellyfin.Api.Auth;
using Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy; using Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy;
using Jellyfin.Api.Auth.RequiresElevationPolicy; using Jellyfin.Api.Auth.RequiresElevationPolicy;
@ -12,8 +11,16 @@ using Microsoft.OpenApi.Models;
namespace Jellyfin.Api.Extensions namespace Jellyfin.Api.Extensions
{ {
/// <summary>
/// API specific extensions for the service collection.
/// </summary>
public static class ApiServiceCollectionExtensions public static class ApiServiceCollectionExtensions
{ {
/// <summary>
/// Adds jellyfin API authorization policies to the DI container.
/// </summary>
/// <param name="serviceCollection">The service collection.</param>
/// <returns>The updated service collection.</returns>
public static IServiceCollection AddJellyfinApiAuthorization(this IServiceCollection serviceCollection) public static IServiceCollection AddJellyfinApiAuthorization(this IServiceCollection serviceCollection)
{ {
serviceCollection.AddSingleton<IAuthorizationHandler, FirstTimeSetupOrElevatedHandler>(); serviceCollection.AddSingleton<IAuthorizationHandler, FirstTimeSetupOrElevatedHandler>();
@ -37,12 +44,23 @@ namespace Jellyfin.Api.Extensions
}); });
} }
/// <summary>
/// Adds custom legacy authentication to the service collection.
/// </summary>
/// <param name="serviceCollection">The service collection.</param>
/// <returns>The updated service collection.</returns>
public static AuthenticationBuilder AddCustomAuthentication(this IServiceCollection serviceCollection) public static AuthenticationBuilder AddCustomAuthentication(this IServiceCollection serviceCollection)
{ {
return serviceCollection.AddAuthentication("CustomAuthentication") return serviceCollection.AddAuthentication("CustomAuthentication")
.AddScheme<AuthenticationSchemeOptions, CustomAuthenticationHandler>("CustomAuthentication", null); .AddScheme<AuthenticationSchemeOptions, CustomAuthenticationHandler>("CustomAuthentication", null);
} }
/// <summary>
/// Extension method for adding the jellyfin API to the service collection.
/// </summary>
/// <param name="serviceCollection">The service collection.</param>
/// <param name="baseUrl">The base url for the API.</param>
/// <returns>The MVC builder.</returns>
public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, string baseUrl) public static IMvcBuilder AddJellyfinApi(this IServiceCollection serviceCollection, string baseUrl)
{ {
return serviceCollection.AddMvc(opts => return serviceCollection.AddMvc(opts =>
@ -55,12 +73,18 @@ namespace Jellyfin.Api.Extensions
opts.UseGeneralRoutePrefix(baseUrl); opts.UseGeneralRoutePrefix(baseUrl);
}) })
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
// Clear app parts to avoid other assemblies being picked up // Clear app parts to avoid other assemblies being picked up
.ConfigureApplicationPartManager(a => a.ApplicationParts.Clear()) .ConfigureApplicationPartManager(a => a.ApplicationParts.Clear())
.AddApplicationPart(typeof(StartupController).Assembly) .AddApplicationPart(typeof(StartupController).Assembly)
.AddControllersAsServices(); .AddControllersAsServices();
} }
/// <summary>
/// Adds Swagger to the service collection.
/// </summary>
/// <param name="serviceCollection">The service collection.</param>
/// <returns>The updated service collection.</returns>
public static IServiceCollection AddJellyfinApiSwagger(this IServiceCollection serviceCollection) public static IServiceCollection AddJellyfinApiSwagger(this IServiceCollection serviceCollection)
{ {
return serviceCollection.AddSwaggerGen(c => return serviceCollection.AddSwaggerGen(c =>

View file

@ -2,6 +2,8 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -15,4 +17,16 @@
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
</ItemGroup> </ItemGroup>
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project> </Project>

View file

@ -1,9 +1,23 @@
namespace Jellyfin.Api.Models.Startup namespace Jellyfin.Api.Models.Startup
{ {
/// <summary>
/// The startup configuration DTO.
/// </summary>
public class StartupConfigurationDto public class StartupConfigurationDto
{ {
/// <summary>
/// Gets or sets UI language culture.
/// </summary>
public string UICulture { get; set; } public string UICulture { get; set; }
/// <summary>
/// Gets or sets the metadata country code.
/// </summary>
public string MetadataCountryCode { get; set; } public string MetadataCountryCode { get; set; }
/// <summary>
/// Gets or sets the preferred language for the metadata.
/// </summary>
public string PreferredMetadataLanguage { get; set; } public string PreferredMetadataLanguage { get; set; }
} }
} }

View file

@ -1,8 +1,18 @@
namespace Jellyfin.Api.Models.Startup namespace Jellyfin.Api.Models.Startup
{ {
/// <summary>
/// The startup user DTO.
/// </summary>
public class StartupUserDto public class StartupUserDto
{ {
/// <summary>
/// Gets or sets the username.
/// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary>
/// Gets or sets the user's password.
/// </summary>
public string Password { get; set; } public string Password { get; set; }
} }
} }

View file

@ -3,10 +3,18 @@ using System.Linq;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace Emby.Server.Implementations namespace Jellyfin.Api
{ {
/// <summary>
/// Route prefixing for ASP.NET MVC.
/// </summary>
public static class MvcRoutePrefix public static class MvcRoutePrefix
{ {
/// <summary>
/// Adds route prefixes to the MVC conventions.
/// </summary>
/// <param name="opts">The MVC options.</param>
/// <param name="prefixes">The list of prefixes.</param>
public static void UseGeneralRoutePrefix(this MvcOptions opts, params string[] prefixes) public static void UseGeneralRoutePrefix(this MvcOptions opts, params string[] prefixes)
{ {
opts.Conventions.Insert(0, new RoutePrefixConvention(prefixes)); opts.Conventions.Insert(0, new RoutePrefixConvention(prefixes));