Introduced IBasicAuthService

This commit is contained in:
Bjarke Berg
2021-08-06 09:51:08 +02:00
parent 4f2cb09939
commit 0e7f9d93ca
9 changed files with 104 additions and 38 deletions

View File

@@ -46,6 +46,7 @@ namespace JsonSchema
public UnattendedSettings Unattended { get; set; }
public RichTextEditorSettings RichTextEditor { get; set; }
public RuntimeMinificationSettings RuntimeMinification { get; set; }
public BasicAuthSettings BasicAuth { get; set; }
}
/// <summary>

View File

@@ -0,0 +1,33 @@
using System.Net;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
namespace Umbraco.Cms.Core.Services
{
public class BasicAuthService : IBasicAuthService
{
private BasicAuthSettings _basicAuthSettings;
public BasicAuthService(IOptionsMonitor<BasicAuthSettings> optionsMonitor)
{
_basicAuthSettings = optionsMonitor.CurrentValue;
optionsMonitor.OnChange(basicAuthSettings => _basicAuthSettings = basicAuthSettings);
}
public bool IsBasicAuthEnabled() => _basicAuthSettings.Enabled;
public bool IsIpAllowListed(IPAddress clientIpAddress)
{
foreach (var allowedIpString in _basicAuthSettings.AllowedIPs)
{
if(IPAddress.TryParse(allowedIpString, out var allowedIp) && clientIpAddress.Equals(allowedIp))
{
return true;
};
}
return false;
}
}
}

View File

@@ -0,0 +1,10 @@
using System.Net;
namespace Umbraco.Cms.Core.Services
{
public interface IBasicAuthService
{
bool IsBasicAuthEnabled();
bool IsIpAllowListed(IPAddress clientIpAddress);
}
}

View File

@@ -40,6 +40,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
builder.Services.AddUnique<IDomainService, DomainService>();
builder.Services.AddUnique<IAuditService, AuditService>();
builder.Services.AddUnique<ICacheInstructionService, CacheInstructionService>();
builder.Services.AddUnique<IBasicAuthService, BasicAuthService>();
builder.Services.AddUnique<ITagService, TagService>();
builder.Services.AddUnique<IContentService, ContentService>();
builder.Services.AddUnique<IUserService, UserService>();

View File

@@ -0,0 +1,35 @@
using System.Linq;
using System.Net;
using Microsoft.Extensions.Options;
using Moq;
using NUnit.Framework;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Services
{
[TestFixture]
public class BasicAuthServiceTests
{
[TestCase(true, ExpectedResult = true)]
[TestCase(false, ExpectedResult = false)]
public bool IsBasicAuthEnabled(bool enabled)
{
var sut = new BasicAuthService(Mock.Of<IOptionsMonitor<BasicAuthSettings>>(_ => _.CurrentValue == new BasicAuthSettings() {Enabled = enabled}));
return sut.IsBasicAuthEnabled();
}
[TestCase("::1", "1.1.1.1", ExpectedResult = false)]
[TestCase("::1", "1.1.1.1, ::1", ExpectedResult = true)]
[TestCase("127.0.0.1", "127.0.0.1, ::1", ExpectedResult = true)]
[TestCase("127.0.0.1", "", ExpectedResult = false)]
public bool IsBasicAuthEnabled(string clientIpAddress, string commaSeperatedAllowlist)
{
var allowedIPs = commaSeperatedAllowlist.Split(",").Select(x=>x.Trim()).ToArray();
var sut = new BasicAuthService(Mock.Of<IOptionsMonitor<BasicAuthSettings>>(_ => _.CurrentValue == new BasicAuthSettings() {AllowedIPs = allowedIPs}));
return sut.IsIpAllowListed(IPAddress.Parse(clientIpAddress));
}
}
}

View File

@@ -30,7 +30,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Security
var mgr = new BackOfficeCookieManager(
Mock.Of<IUmbracoContextAccessor>(),
runtime,
new UmbracoRequestPaths(Options.Create(globalSettings), TestHelper.GetHostingEnvironment()));
new UmbracoRequestPaths(Options.Create(globalSettings), TestHelper.GetHostingEnvironment()),
Mock.Of<IBasicAuthService>());
var result = mgr.ShouldAuthenticateRequest("/umbraco");
@@ -48,7 +49,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Security
runtime,
new UmbracoRequestPaths(
Options.Create(globalSettings),
Mock.Of<IHostingEnvironment>(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco")));
Mock.Of<IHostingEnvironment>(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco")),
Mock.Of<IBasicAuthService>());
var result = mgr.ShouldAuthenticateRequest("/umbraco");
@@ -69,7 +71,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Security
runtime,
new UmbracoRequestPaths(
Options.Create(globalSettings),
Mock.Of<IHostingEnvironment>(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && x.ToAbsolute(Constants.SystemDirectories.Install) == "/install")));
Mock.Of<IHostingEnvironment>(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && x.ToAbsolute(Constants.SystemDirectories.Install) == "/install")),
Mock.Of<IBasicAuthService>());
var result = mgr.ShouldAuthenticateRequest(remainingTimeoutSecondsPath);
Assert.IsTrue(result);
@@ -90,7 +93,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Security
runtime,
new UmbracoRequestPaths(
Options.Create(globalSettings),
Mock.Of<IHostingEnvironment>(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && x.ToAbsolute(Constants.SystemDirectories.Install) == "/install")));
Mock.Of<IHostingEnvironment>(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && x.ToAbsolute(Constants.SystemDirectories.Install) == "/install")),
Mock.Of<IBasicAuthService>());
var result = mgr.ShouldAuthenticateRequest("/notbackoffice");
Assert.IsFalse(result);

View File

@@ -1,15 +1,11 @@
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.BackOffice.Security;
using Umbraco.Extensions;
@@ -22,32 +18,28 @@ namespace Umbraco.Cms.Web.Common.Middleware
/// </summary>
public class BasicAuthAuthenticationMiddleware : IMiddleware
{
private readonly ILogger<BasicAuthAuthenticationMiddleware> _logger;
private readonly IOptionsSnapshot<BasicAuthSettings> _basicAuthSettings;
private readonly IRuntimeState _runtimeState;
private readonly IBasicAuthService _basicAuthService;
public BasicAuthAuthenticationMiddleware(
ILogger<BasicAuthAuthenticationMiddleware> logger,
IOptionsSnapshot<BasicAuthSettings> basicAuthSettings,
IRuntimeState runtimeState)
IRuntimeState runtimeState,
IBasicAuthService basicAuthService)
{
_logger = logger;
_basicAuthSettings = basicAuthSettings;
_runtimeState = runtimeState;
_basicAuthService = basicAuthService;
}
/// <inheritdoc />
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var options = _basicAuthSettings.Value;
if (!options.Enabled || _runtimeState.Level < RuntimeLevel.Run)
if (_runtimeState.Level < RuntimeLevel.Run || !_basicAuthService.IsBasicAuthEnabled())
{
await next(context);
return;
}
var clientIPAddress = context.Connection.RemoteIpAddress;
if (IsIpAllowListed(clientIPAddress, options.AllowedIPs))
if (_basicAuthService.IsIpAllowListed(clientIPAddress))
{
await next(context);
return;
@@ -98,18 +90,7 @@ namespace Umbraco.Cms.Web.Common.Middleware
}
}
private bool IsIpAllowListed(IPAddress clientIpAddress, string[] allowlist)
{
foreach (var allowedIpString in allowlist)
{
if(IPAddress.TryParse(allowedIpString, out var allowedIp) && clientIpAddress.Equals(allowedIp))
{
return true;
};
}
return false;
}
private static void SetUnauthorizedHeader(HttpContext context)
{

View File

@@ -25,7 +25,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security
private readonly IRuntimeState _runtime;
private readonly string[] _explicitPaths;
private readonly UmbracoRequestPaths _umbracoRequestPaths;
private readonly IOptionsMonitor<BasicAuthSettings> _basicAuthSettingsMonitor;
private readonly IBasicAuthService _basicAuthService;
/// <summary>
/// Initializes a new instance of the <see cref="BackOfficeCookieManager"/> class.
@@ -34,8 +34,8 @@ namespace Umbraco.Cms.Web.BackOffice.Security
IUmbracoContextAccessor umbracoContextAccessor,
IRuntimeState runtime,
UmbracoRequestPaths umbracoRequestPaths,
IOptionsMonitor<BasicAuthSettings> basicAuthSettings)
: this(umbracoContextAccessor, runtime, null, umbracoRequestPaths, basicAuthSettings)
IBasicAuthService basicAuthService)
: this(umbracoContextAccessor, runtime, null, umbracoRequestPaths, basicAuthService)
{
}
@@ -47,13 +47,13 @@ namespace Umbraco.Cms.Web.BackOffice.Security
IRuntimeState runtime,
IEnumerable<string> explicitPaths,
UmbracoRequestPaths umbracoRequestPaths,
IOptionsMonitor<BasicAuthSettings> basicAuthSettingsMonitor)
IBasicAuthService basicAuthService)
{
_umbracoContextAccessor = umbracoContextAccessor;
_runtime = runtime;
_explicitPaths = explicitPaths?.ToArray();
_umbracoRequestPaths = umbracoRequestPaths;
_basicAuthSettingsMonitor = basicAuthSettingsMonitor;
_basicAuthService = basicAuthService;
}
/// <summary>
@@ -94,7 +94,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security
return true;
}
if (_basicAuthSettingsMonitor.CurrentValue.Enabled)
if (_basicAuthService.IsBasicAuthEnabled())
{
return true;
}

View File

@@ -34,6 +34,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security
private readonly IIpResolver _ipResolver;
private readonly ISystemClock _systemClock;
private readonly UmbracoRequestPaths _umbracoRequestPaths;
private readonly IBasicAuthService _basicAuthService;
private readonly IOptionsMonitor<BasicAuthSettings> _optionsSnapshot;
/// <summary>
@@ -61,7 +62,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security
IIpResolver ipResolver,
ISystemClock systemClock,
UmbracoRequestPaths umbracoRequestPaths,
IOptionsMonitor<BasicAuthSettings> optionsSnapshot)
IBasicAuthService basicAuthService)
{
_serviceProvider = serviceProvider;
_umbracoContextAccessor = umbracoContextAccessor;
@@ -74,7 +75,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security
_ipResolver = ipResolver;
_systemClock = systemClock;
_umbracoRequestPaths = umbracoRequestPaths;
_optionsSnapshot = optionsSnapshot;
_basicAuthService = basicAuthService;
}
/// <inheritdoc />
@@ -119,7 +120,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security
_umbracoContextAccessor,
_runtimeState,
_umbracoRequestPaths,
_optionsSnapshot
_basicAuthService
);
options.Events = new CookieAuthenticationEvents