New: Setting to disable authentication for local addresses
This commit is contained in:
parent
05ee4e6449
commit
b154b00c61
|
@ -16,6 +16,11 @@ const authenticationMethodOptions = [
|
||||||
{ key: 'forms', value: 'Forms (Login Page)' }
|
{ key: 'forms', value: 'Forms (Login Page)' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const authenticationRequiredOptions = [
|
||||||
|
{ key: 'enabled', value: 'Enabled' },
|
||||||
|
{ key: 'disabledForLocalAddresses', value: 'Disabled for Local Addresses' }
|
||||||
|
];
|
||||||
|
|
||||||
const certificateValidationOptions = [
|
const certificateValidationOptions = [
|
||||||
{ key: 'enabled', value: 'Enabled' },
|
{ key: 'enabled', value: 'Enabled' },
|
||||||
{ key: 'disabledForLocalAddresses', value: 'Disabled for Local Addresses' },
|
{ key: 'disabledForLocalAddresses', value: 'Disabled for Local Addresses' },
|
||||||
|
@ -67,6 +72,7 @@ class SecuritySettings extends Component {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
authenticationMethod,
|
authenticationMethod,
|
||||||
|
authenticationRequired,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
apiKey,
|
apiKey,
|
||||||
|
@ -91,7 +97,24 @@ class SecuritySettings extends Component {
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
{
|
{
|
||||||
authenticationEnabled &&
|
authenticationEnabled ?
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Authentication Required</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.SELECT}
|
||||||
|
name="authenticationRequired"
|
||||||
|
values={authenticationRequiredOptions}
|
||||||
|
helpText="Change which requests authentication is required for. Do not change unless you understand the risks."
|
||||||
|
onChange={onInputChange}
|
||||||
|
{...authenticationRequired}
|
||||||
|
/>
|
||||||
|
</FormGroup> :
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
authenticationEnabled ?
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>Username</FormLabel>
|
<FormLabel>Username</FormLabel>
|
||||||
|
|
||||||
|
@ -101,11 +124,12 @@ class SecuritySettings extends Component {
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
{...username}
|
{...username}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup> :
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
authenticationEnabled &&
|
authenticationEnabled ?
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>Password</FormLabel>
|
<FormLabel>Password</FormLabel>
|
||||||
|
|
||||||
|
@ -115,7 +139,8 @@ class SecuritySettings extends Component {
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
{...password}
|
{...password}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup> :
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
@ -46,7 +46,7 @@ namespace NzbDrone.Automation.Test
|
||||||
|
|
||||||
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger());
|
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger());
|
||||||
_runner.KillAll();
|
_runner.KillAll();
|
||||||
_runner.Start();
|
_runner.Start(true);
|
||||||
|
|
||||||
driver.Url = "http://localhost:8989";
|
driver.Url = "http://localhost:8989";
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace NzbDrone.Core.Authentication
|
||||||
|
{
|
||||||
|
public enum AuthenticationRequiredType
|
||||||
|
{
|
||||||
|
Enabled = 0,
|
||||||
|
DisabledForLocalAddresses = 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
namespace NzbDrone.Core.Authentication
|
namespace NzbDrone.Core.Authentication
|
||||||
{
|
{
|
||||||
public enum AuthenticationType
|
public enum AuthenticationType
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
Basic = 1,
|
Basic = 1,
|
||||||
Forms = 2
|
Forms = 2,
|
||||||
|
External = 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace NzbDrone.Core.Configuration
|
||||||
bool EnableSsl { get; }
|
bool EnableSsl { get; }
|
||||||
bool LaunchBrowser { get; }
|
bool LaunchBrowser { get; }
|
||||||
AuthenticationType AuthenticationMethod { get; }
|
AuthenticationType AuthenticationMethod { get; }
|
||||||
|
AuthenticationRequiredType AuthenticationRequired { get; }
|
||||||
bool AnalyticsEnabled { get; }
|
bool AnalyticsEnabled { get; }
|
||||||
string LogLevel { get; }
|
string LogLevel { get; }
|
||||||
string ConsoleLogLevel { get; }
|
string ConsoleLogLevel { get; }
|
||||||
|
@ -181,6 +182,8 @@ namespace NzbDrone.Core.Configuration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthenticationRequiredType AuthenticationRequired => GetValueEnum("AuthenticationRequired", AuthenticationRequiredType.Enabled);
|
||||||
|
|
||||||
public bool AnalyticsEnabled => GetValueBoolean("AnalyticsEnabled", true, persist: false);
|
public bool AnalyticsEnabled => GetValueBoolean("AnalyticsEnabled", true, persist: false);
|
||||||
|
|
||||||
public string Branch => GetValue("Branch", "main").ToLowerInvariant();
|
public string Branch => GetValue("Branch", "main").ToLowerInvariant();
|
||||||
|
|
|
@ -102,6 +102,8 @@ namespace NzbDrone.Host
|
||||||
.PersistKeysToFileSystem(new DirectoryInfo(Configuration["dataProtectionFolder"]));
|
.PersistKeysToFileSystem(new DirectoryInfo(Configuration["dataProtectionFolder"]));
|
||||||
|
|
||||||
services.AddSingleton<IAuthorizationPolicyProvider, UiAuthorizationPolicyProvider>();
|
services.AddSingleton<IAuthorizationPolicyProvider, UiAuthorizationPolicyProvider>();
|
||||||
|
services.AddSingleton<IAuthorizationHandler, UiAuthorizationHandler>();
|
||||||
|
|
||||||
services.AddAuthorization(options =>
|
services.AddAuthorization(options =>
|
||||||
{
|
{
|
||||||
options.AddPolicy("SignalR", policy =>
|
options.AddPolicy("SignalR", policy =>
|
||||||
|
|
|
@ -31,12 +31,12 @@ namespace NzbDrone.Test.Common
|
||||||
Port = port;
|
Port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start(bool enableAuth = false)
|
||||||
{
|
{
|
||||||
AppData = Path.Combine(TestContext.CurrentContext.TestDirectory, "_intg_" + TestBase.GetUID());
|
AppData = Path.Combine(TestContext.CurrentContext.TestDirectory, "_intg_" + TestBase.GetUID());
|
||||||
Directory.CreateDirectory(AppData);
|
Directory.CreateDirectory(AppData);
|
||||||
|
|
||||||
GenerateConfigFile();
|
GenerateConfigFile(enableAuth);
|
||||||
|
|
||||||
string consoleExe;
|
string consoleExe;
|
||||||
if (OsInfo.IsWindows)
|
if (OsInfo.IsWindows)
|
||||||
|
@ -146,7 +146,7 @@ namespace NzbDrone.Test.Common
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateConfigFile()
|
private void GenerateConfigFile(bool enableAuth)
|
||||||
{
|
{
|
||||||
var configFile = Path.Combine(AppData, "config.xml");
|
var configFile = Path.Combine(AppData, "config.xml");
|
||||||
|
|
||||||
|
@ -159,6 +159,8 @@ namespace NzbDrone.Test.Common
|
||||||
new XElement(nameof(ConfigFileProvider.ApiKey), apiKey),
|
new XElement(nameof(ConfigFileProvider.ApiKey), apiKey),
|
||||||
new XElement(nameof(ConfigFileProvider.LogLevel), "trace"),
|
new XElement(nameof(ConfigFileProvider.LogLevel), "trace"),
|
||||||
new XElement(nameof(ConfigFileProvider.AnalyticsEnabled), false),
|
new XElement(nameof(ConfigFileProvider.AnalyticsEnabled), false),
|
||||||
|
new XElement(nameof(ConfigFileProvider.AuthenticationMethod), enableAuth ? "Forms" : "None"),
|
||||||
|
new XElement(nameof(ConfigFileProvider.AuthenticationRequired), "DisabledForLocalAddresses"),
|
||||||
new XElement(nameof(ConfigFileProvider.Port), Port)));
|
new XElement(nameof(ConfigFileProvider.Port), Port)));
|
||||||
|
|
||||||
var data = xDoc.ToString();
|
var data = xDoc.ToString();
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace Sonarr.Api.V3.Config
|
||||||
public bool EnableSsl { get; set; }
|
public bool EnableSsl { get; set; }
|
||||||
public bool LaunchBrowser { get; set; }
|
public bool LaunchBrowser { get; set; }
|
||||||
public AuthenticationType AuthenticationMethod { get; set; }
|
public AuthenticationType AuthenticationMethod { get; set; }
|
||||||
|
public AuthenticationRequiredType AuthenticationRequired { get; set; }
|
||||||
public bool AnalyticsEnabled { get; set; }
|
public bool AnalyticsEnabled { get; set; }
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
@ -56,6 +57,7 @@ namespace Sonarr.Api.V3.Config
|
||||||
EnableSsl = model.EnableSsl,
|
EnableSsl = model.EnableSsl,
|
||||||
LaunchBrowser = model.LaunchBrowser,
|
LaunchBrowser = model.LaunchBrowser,
|
||||||
AuthenticationMethod = model.AuthenticationMethod,
|
AuthenticationMethod = model.AuthenticationMethod,
|
||||||
|
AuthenticationRequired = model.AuthenticationRequired,
|
||||||
AnalyticsEnabled = model.AnalyticsEnabled,
|
AnalyticsEnabled = model.AnalyticsEnabled,
|
||||||
|
|
||||||
//Username
|
//Username
|
||||||
|
|
|
@ -13,6 +13,7 @@ namespace Sonarr.Http.Authentication
|
||||||
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
|
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
|
||||||
{
|
{
|
||||||
public const string DefaultScheme = "API Key";
|
public const string DefaultScheme = "API Key";
|
||||||
|
|
||||||
public string Scheme => DefaultScheme;
|
public string Scheme => DefaultScheme;
|
||||||
public string AuthenticationType = DefaultScheme;
|
public string AuthenticationType = DefaultScheme;
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,16 @@ namespace Sonarr.Http.Authentication
|
||||||
return authenticationBuilder.AddScheme<AuthenticationSchemeOptions, NoAuthenticationHandler>(name, options => { });
|
return authenticationBuilder.AddScheme<AuthenticationSchemeOptions, NoAuthenticationHandler>(name, options => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AuthenticationBuilder AddExternal(this AuthenticationBuilder authenticationBuilder, string name)
|
||||||
|
{
|
||||||
|
return authenticationBuilder.AddScheme<AuthenticationSchemeOptions, NoAuthenticationHandler>(name, options => { });
|
||||||
|
}
|
||||||
|
|
||||||
public static AuthenticationBuilder AddAppAuthentication(this IServiceCollection services)
|
public static AuthenticationBuilder AddAppAuthentication(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
return services.AddAuthentication()
|
return services.AddAuthentication()
|
||||||
.AddNone(AuthenticationType.None.ToString())
|
.AddNone(AuthenticationType.None.ToString())
|
||||||
|
.AddExternal(AuthenticationType.External.ToString())
|
||||||
.AddBasic(AuthenticationType.Basic.ToString())
|
.AddBasic(AuthenticationType.Basic.ToString())
|
||||||
.AddCookie(AuthenticationType.Forms.ToString(), options =>
|
.AddCookie(AuthenticationType.Forms.ToString(), options =>
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
using Microsoft.AspNetCore.Authorization.Infrastructure;
|
||||||
|
|
||||||
|
namespace NzbDrone.Http.Authentication
|
||||||
|
{
|
||||||
|
public class BypassableDenyAnonymousAuthorizationRequirement : DenyAnonymousAuthorizationRequirement
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Authentication;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Configuration.Events;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
using Sonarr.Http.Extensions;
|
||||||
|
|
||||||
|
namespace NzbDrone.Http.Authentication
|
||||||
|
{
|
||||||
|
public class UiAuthorizationHandler : AuthorizationHandler<BypassableDenyAnonymousAuthorizationRequirement>, IAuthorizationRequirement, IHandle<ConfigSavedEvent>
|
||||||
|
{
|
||||||
|
private readonly IConfigFileProvider _configService;
|
||||||
|
private static AuthenticationRequiredType _authenticationRequired;
|
||||||
|
|
||||||
|
public UiAuthorizationHandler(IConfigFileProvider configService)
|
||||||
|
{
|
||||||
|
_configService = configService;
|
||||||
|
_authenticationRequired = configService.AuthenticationRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, BypassableDenyAnonymousAuthorizationRequirement requirement)
|
||||||
|
{
|
||||||
|
if (_authenticationRequired == AuthenticationRequiredType.DisabledForLocalAddresses)
|
||||||
|
{
|
||||||
|
if (context.Resource is HttpContext httpContext &&
|
||||||
|
IPAddress.TryParse(httpContext.GetRemoteIP(), out var ipAddress) &&
|
||||||
|
ipAddress.IsLocalAddress())
|
||||||
|
{
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ConfigSavedEvent message)
|
||||||
|
{
|
||||||
|
_authenticationRequired = _configService.AuthenticationRequired;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,7 +29,8 @@ namespace NzbDrone.Http.Authentication
|
||||||
if (policyName.Equals(POLICY_NAME, StringComparison.OrdinalIgnoreCase))
|
if (policyName.Equals(POLICY_NAME, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var policy = new AuthorizationPolicyBuilder(_config.AuthenticationMethod.ToString())
|
var policy = new AuthorizationPolicyBuilder(_config.AuthenticationMethod.ToString())
|
||||||
.RequireAuthenticatedUser();
|
.AddRequirements(new BypassableDenyAnonymousAuthorizationRequirement());
|
||||||
|
|
||||||
return Task.FromResult(policy.Build());
|
return Task.FromResult(policy.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue