mirror of
https://github.com/Readarr/Readarr.git
synced 2026-03-05 13:20:32 -05:00
New: Setting to disable authentication for local addresses
(cherry picked from commit b154b00c6156512e7fbd0a2c4833c116a74f23ca)
This commit is contained in:
@@ -17,6 +17,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' },
|
||||||
@@ -68,6 +73,7 @@ class SecuritySettings extends Component {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
authenticationMethod,
|
authenticationMethod,
|
||||||
|
authenticationRequired,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
apiKey,
|
apiKey,
|
||||||
@@ -94,7 +100,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>
|
<FormLabel>
|
||||||
{translate('Username')}
|
{translate('Username')}
|
||||||
@@ -106,11 +129,12 @@ class SecuritySettings extends Component {
|
|||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
{...username}
|
{...username}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup> :
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
authenticationEnabled &&
|
authenticationEnabled ?
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{translate('Password')}
|
{translate('Password')}
|
||||||
@@ -122,7 +146,8 @@ class SecuritySettings extends Component {
|
|||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
{...password}
|
{...password}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup> :
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace NzbDrone.Automation.Test
|
|||||||
|
|
||||||
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger(), null);
|
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger(), null);
|
||||||
_runner.KillAll();
|
_runner.KillAll();
|
||||||
_runner.Start();
|
_runner.Start(true);
|
||||||
|
|
||||||
driver.Url = "http://localhost:8787";
|
driver.Url = "http://localhost:8787";
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,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; }
|
||||||
@@ -190,6 +191,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);
|
||||||
|
|
||||||
// TODO: Change back to "master" for the first stable release
|
// TODO: Change back to "master" for the first stable release
|
||||||
|
|||||||
@@ -171,6 +171,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 =>
|
||||||
|
|||||||
@@ -38,12 +38,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 readarrConsoleExe;
|
string readarrConsoleExe;
|
||||||
if (OsInfo.IsWindows)
|
if (OsInfo.IsWindows)
|
||||||
@@ -178,7 +178,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");
|
||||||
|
|
||||||
@@ -191,6 +191,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 Readarr.Api.V1.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; }
|
||||||
@@ -57,6 +58,7 @@ namespace Readarr.Api.V1.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 Readarr.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 Readarr.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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/Readarr.Http/Authentication/UiAuthorizationHandler.cs
Normal file
45
src/Readarr.Http/Authentication/UiAuthorizationHandler.cs
Normal file
@@ -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 Readarr.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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user