Moved all UnitTests to a single project. Implemented parts of AspNetCore UnitTests.
This commit is contained in:
@@ -0,0 +1,346 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AMWD.Common.AspNetCore.BasicAuthentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
|
||||
namespace UnitTests.AspNetCore.Attributes
|
||||
{
|
||||
[TestClass]
|
||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||
public class BasicAuthenticationAttributeTests
|
||||
{
|
||||
private Mock<IHeaderDictionary> requestHeaderMock;
|
||||
private Mock<IHeaderDictionary> responseHeaderMock;
|
||||
|
||||
private Mock<HttpRequest> requestMock;
|
||||
private Mock<HttpResponse> responseMock;
|
||||
|
||||
private Mock<HttpContext> contextMock;
|
||||
|
||||
private Dictionary<string, string> requestHeaders;
|
||||
private string validatorRealm;
|
||||
private ClaimsPrincipal validatorResult;
|
||||
|
||||
private string responseHeaderAuthCallback;
|
||||
|
||||
[TestInitialize]
|
||||
public void InitializeTest()
|
||||
{
|
||||
requestHeaders = new Dictionary<string, string>();
|
||||
validatorRealm = null;
|
||||
validatorResult = null;
|
||||
|
||||
responseHeaderAuthCallback = null;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldValidateViaUsernamePassword()
|
||||
{
|
||||
// arrange
|
||||
var attribute = new BasicAuthenticationAttribute
|
||||
{
|
||||
Username = "user",
|
||||
Password = "password"
|
||||
};
|
||||
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}:{attribute.Password}"))}");
|
||||
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
await attribute.OnAuthorizationAsync(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNull(context.Result);
|
||||
Assert.IsTrue(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldValidateViaValidator()
|
||||
{
|
||||
// arrange
|
||||
var attribute = new BasicAuthenticationAttribute();
|
||||
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}:{attribute.Password}"))}");
|
||||
validatorResult = new ClaimsPrincipal();
|
||||
|
||||
var context = GetContext(hasValidator: true);
|
||||
|
||||
// act
|
||||
await attribute.OnAuthorizationAsync(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNull(context.Result);
|
||||
Assert.IsTrue(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldAllowAnonymous()
|
||||
{
|
||||
// arrange
|
||||
var attribute = new BasicAuthenticationAttribute
|
||||
{
|
||||
Username = "user",
|
||||
Password = "password"
|
||||
};
|
||||
var context = GetContext(isAnonymousAllowed: true);
|
||||
|
||||
// act
|
||||
await attribute.OnAuthorizationAsync(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNull(context.Result);
|
||||
Assert.IsTrue(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldAskOnUsernamePasswordWithoutRealm()
|
||||
{
|
||||
// arrange
|
||||
var attribute = new BasicAuthenticationAttribute
|
||||
{
|
||||
Username = "user",
|
||||
Password = "password"
|
||||
};
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
await attribute.OnAuthorizationAsync(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(401, ((StatusCodeResult)context.Result).StatusCode);
|
||||
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||
Assert.AreEqual("Basic", responseHeaderAuthCallback);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldAskOnUsernamePasswordWithRealm()
|
||||
{
|
||||
// arrange
|
||||
var attribute = new BasicAuthenticationAttribute
|
||||
{
|
||||
Username = "user",
|
||||
Password = "password",
|
||||
Realm = "re:al\"m"
|
||||
};
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
await attribute.OnAuthorizationAsync(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(401, ((StatusCodeResult)context.Result).StatusCode);
|
||||
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||
Assert.AreEqual("Basic realm=\"re:alm\"", responseHeaderAuthCallback);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldAskOnUsernamePasswordWrongUser()
|
||||
{
|
||||
// arrange
|
||||
var attribute = new BasicAuthenticationAttribute
|
||||
{
|
||||
Username = "user",
|
||||
Password = "password"
|
||||
};
|
||||
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}a:{attribute.Password}"))}");
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
await attribute.OnAuthorizationAsync(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(401, ((StatusCodeResult)context.Result).StatusCode);
|
||||
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||
Assert.AreEqual("Basic", responseHeaderAuthCallback);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldAskOnUsernamePasswordWrongPassword()
|
||||
{
|
||||
// arrange
|
||||
var attribute = new BasicAuthenticationAttribute
|
||||
{
|
||||
Username = "user",
|
||||
Password = "password"
|
||||
};
|
||||
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}:{attribute.Password}a"))}");
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
await attribute.OnAuthorizationAsync(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(401, ((StatusCodeResult)context.Result).StatusCode);
|
||||
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||
Assert.AreEqual("Basic", responseHeaderAuthCallback);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldAskOnValidatorWithRealmOnAttribute()
|
||||
{
|
||||
// arrange
|
||||
var attribute = new BasicAuthenticationAttribute
|
||||
{
|
||||
Realm = "attribute"
|
||||
};
|
||||
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}:{attribute.Password}"))}");
|
||||
var context = GetContext(hasValidator: true);
|
||||
|
||||
// act
|
||||
await attribute.OnAuthorizationAsync(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(401, ((StatusCodeResult)context.Result).StatusCode);
|
||||
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||
Assert.AreEqual("Basic realm=\"attribute\"", responseHeaderAuthCallback);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldAskOnValidatorWithRealmOnValidator()
|
||||
{
|
||||
// arrange
|
||||
validatorRealm = "validator";
|
||||
var attribute = new BasicAuthenticationAttribute();
|
||||
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}:{attribute.Password}"))}");
|
||||
var context = GetContext(hasValidator: true);
|
||||
|
||||
// act
|
||||
await attribute.OnAuthorizationAsync(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(401, ((StatusCodeResult)context.Result).StatusCode);
|
||||
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(responseHeaderAuthCallback));
|
||||
Assert.AreEqual("Basic realm=\"validator\"", responseHeaderAuthCallback);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldReturnInternalError()
|
||||
{
|
||||
// arrange
|
||||
var attribute = new BasicAuthenticationAttribute();
|
||||
requestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{attribute.Username}"))}");
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
await attribute.OnAuthorizationAsync(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(500, ((StatusCodeResult)context.Result).StatusCode);
|
||||
}
|
||||
|
||||
private AuthorizationFilterContext GetContext(bool isAnonymousAllowed = false, bool hasValidator = false)
|
||||
{
|
||||
requestHeaderMock = new Mock<IHeaderDictionary>();
|
||||
foreach (var header in requestHeaders)
|
||||
{
|
||||
requestHeaderMock
|
||||
.Setup(h => h.ContainsKey(header.Key))
|
||||
.Returns(true);
|
||||
requestHeaderMock
|
||||
.Setup(h => h[header.Key])
|
||||
.Returns(header.Value);
|
||||
}
|
||||
|
||||
responseHeaderMock = new Mock<IHeaderDictionary>();
|
||||
responseHeaderMock
|
||||
.SetupSet(h => h["WWW-Authenticate"] = It.IsAny<StringValues>())
|
||||
.Callback<string, StringValues>((key, value) =>
|
||||
{
|
||||
responseHeaderAuthCallback = value;
|
||||
});
|
||||
|
||||
requestMock = new Mock<HttpRequest>();
|
||||
requestMock
|
||||
.Setup(r => r.Headers)
|
||||
.Returns(requestHeaderMock.Object);
|
||||
|
||||
responseMock = new Mock<HttpResponse>();
|
||||
responseMock
|
||||
.Setup(r => r.Headers)
|
||||
.Returns(responseHeaderMock.Object);
|
||||
|
||||
var requestServicesMock = new Mock<IServiceProvider>();
|
||||
|
||||
if (hasValidator)
|
||||
{
|
||||
var validatorMock = new Mock<IBasicAuthenticationValidator>();
|
||||
validatorMock
|
||||
.Setup(v => v.Realm)
|
||||
.Returns(validatorRealm);
|
||||
validatorMock
|
||||
.Setup(v => v.ValidateAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IPAddress>()))
|
||||
.ReturnsAsync(validatorResult);
|
||||
|
||||
requestServicesMock
|
||||
.Setup(rs => rs.GetService(typeof(IBasicAuthenticationValidator)))
|
||||
.Returns(validatorMock.Object);
|
||||
}
|
||||
|
||||
var connectionInfoMock = new Mock<ConnectionInfo>();
|
||||
connectionInfoMock
|
||||
.Setup(ci => ci.RemoteIpAddress)
|
||||
.Returns(IPAddress.Loopback);
|
||||
|
||||
contextMock = new Mock<HttpContext>();
|
||||
contextMock
|
||||
.Setup(c => c.Request)
|
||||
.Returns(requestMock.Object);
|
||||
contextMock
|
||||
.Setup(c => c.Response)
|
||||
.Returns(responseMock.Object);
|
||||
contextMock
|
||||
.Setup(c => c.RequestServices)
|
||||
.Returns(requestServicesMock.Object);
|
||||
contextMock
|
||||
.Setup(c => c.Connection)
|
||||
.Returns(connectionInfoMock.Object);
|
||||
|
||||
var routeDataMock = new Mock<RouteData>();
|
||||
|
||||
var actionDescriptor = new ActionDescriptor
|
||||
{
|
||||
EndpointMetadata = new List<object>()
|
||||
};
|
||||
if (isAnonymousAllowed)
|
||||
actionDescriptor.EndpointMetadata.Add(new AllowAnonymousAttribute());
|
||||
|
||||
return new AuthorizationFilterContext(new ActionContext
|
||||
{
|
||||
HttpContext = contextMock.Object,
|
||||
RouteData = routeDataMock.Object,
|
||||
ActionDescriptor = actionDescriptor,
|
||||
}, new List<IFilterMetadata>());
|
||||
}
|
||||
}
|
||||
}
|
||||
340
UnitTests/AspNetCore/Attributes/IPBlacklistAttributeTests.cs
Normal file
340
UnitTests/AspNetCore/Attributes/IPBlacklistAttributeTests.cs
Normal file
@@ -0,0 +1,340 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
|
||||
namespace UnitTests.AspNetCore.Attributes
|
||||
{
|
||||
[TestClass]
|
||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||
public class IPBlacklistAttributeTests
|
||||
{
|
||||
private Dictionary<string, string> requestHeaders;
|
||||
private Dictionary<object, object> itemsCallback;
|
||||
private string configKey;
|
||||
private bool configExists;
|
||||
private List<string> restrictedIpsConfig;
|
||||
|
||||
[TestInitialize]
|
||||
public void InitializeTest()
|
||||
{
|
||||
requestHeaders = new Dictionary<string, string>();
|
||||
itemsCallback = new Dictionary<object, object>();
|
||||
configKey = null;
|
||||
configExists = false;
|
||||
restrictedIpsConfig = new List<string>();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldAllowOnNoConfiguration()
|
||||
{
|
||||
// arrange
|
||||
var remote = IPAddress.Parse("192.168.178.1");
|
||||
var attribute = new IPBlacklistAttribute();
|
||||
var context = GetContext(remote);
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNull(context.Result);
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldAllowOnWrongConfiguration()
|
||||
{
|
||||
// arrange
|
||||
var remote = IPAddress.Parse("192.168.178.1");
|
||||
var attribute = new IPBlacklistAttribute
|
||||
{
|
||||
RestrictedIpAddresses = "192.168.178:1"
|
||||
};
|
||||
var context = GetContext(remote);
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNull(context.Result);
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldAllowLocalAccess()
|
||||
{
|
||||
// arrange
|
||||
var attribute = new IPBlacklistAttribute
|
||||
{
|
||||
RestrictLocalAccess = false,
|
||||
RestrictedIpAddresses = "127.0.0.0/8"
|
||||
};
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNull(context.Result);
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldBlockLocalAccess()
|
||||
{
|
||||
// arrange
|
||||
var attribute = new IPBlacklistAttribute
|
||||
{
|
||||
RestrictLocalAccess = true,
|
||||
RestrictedIpAddresses = ",127.0.0.0/8"
|
||||
};
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("192.168.178.10")]
|
||||
[DataRow("192.168.178.20")]
|
||||
public void ShouldBlockSpecificAddress(string address)
|
||||
{
|
||||
// arrange
|
||||
var remote = IPAddress.Parse(address);
|
||||
var attribute = new IPBlacklistAttribute
|
||||
{
|
||||
RestrictLocalAccess = true,
|
||||
RestrictedIpAddresses = "127.0.0.0/8,192.168.178.10"
|
||||
};
|
||||
var context = GetContext(remote);
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
if (address == "192.168.178.10")
|
||||
{
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsNull(context.Result);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldAllowLocalAccessConfig()
|
||||
{
|
||||
// arrange
|
||||
configKey = "Black:List";
|
||||
configExists = true;
|
||||
restrictedIpsConfig.Add("127.0.0.0/8");
|
||||
restrictedIpsConfig.Add("192.168.178.10");
|
||||
var attribute = new IPBlacklistAttribute
|
||||
{
|
||||
RestrictLocalAccess = false,
|
||||
ConfigurationKey = configKey
|
||||
};
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNull(context.Result);
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldBlockLocalAccessConfig()
|
||||
{
|
||||
// arrange
|
||||
configKey = "Black:List";
|
||||
configExists = true;
|
||||
restrictedIpsConfig.Add("");
|
||||
restrictedIpsConfig.Add("127.0.0.0/8");
|
||||
restrictedIpsConfig.Add("192.168.178.10");
|
||||
var attribute = new IPBlacklistAttribute
|
||||
{
|
||||
RestrictLocalAccess = true,
|
||||
ConfigurationKey = configKey
|
||||
};
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("192.168.178.10")]
|
||||
[DataRow("192.168.178.20")]
|
||||
public void ShouldBlockSpecificAddressConfig(string address)
|
||||
{
|
||||
// arrange
|
||||
configKey = "Black:List";
|
||||
configExists = true;
|
||||
restrictedIpsConfig.Add("127.0.0.0/8");
|
||||
restrictedIpsConfig.Add("192.168.178.10");
|
||||
var attribute = new IPBlacklistAttribute
|
||||
{
|
||||
RestrictLocalAccess = true,
|
||||
ConfigurationKey = configKey
|
||||
};
|
||||
var remote = IPAddress.Parse(address);
|
||||
var context = GetContext(remote);
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
if (address == "192.168.178.10")
|
||||
{
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsNull(context.Result);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldAllowOnMissingConfiguration()
|
||||
{
|
||||
// arrange
|
||||
configKey = "Black:List";
|
||||
configExists = false;
|
||||
var attribute = new IPBlacklistAttribute
|
||||
{
|
||||
RestrictLocalAccess = true,
|
||||
ConfigurationKey = configKey
|
||||
};
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNull(context.Result);
|
||||
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
private ActionExecutingContext GetContext(IPAddress remote = null)
|
||||
{
|
||||
var requestHeaderMock = new Mock<IHeaderDictionary>();
|
||||
foreach (var header in requestHeaders)
|
||||
{
|
||||
requestHeaderMock
|
||||
.Setup(h => h.ContainsKey(header.Key))
|
||||
.Returns(true);
|
||||
requestHeaderMock
|
||||
.Setup(h => h[header.Key])
|
||||
.Returns(header.Value);
|
||||
}
|
||||
|
||||
var requestMock = new Mock<HttpRequest>();
|
||||
requestMock
|
||||
.Setup(r => r.Headers)
|
||||
.Returns(requestHeaderMock.Object);
|
||||
|
||||
var connectionInfoMock = new Mock<ConnectionInfo>();
|
||||
connectionInfoMock
|
||||
.Setup(ci => ci.LocalIpAddress)
|
||||
.Returns(IPAddress.Loopback);
|
||||
connectionInfoMock
|
||||
.Setup(ci => ci.RemoteIpAddress)
|
||||
.Returns(remote ?? IPAddress.Loopback);
|
||||
|
||||
var itemsMock = new Mock<IDictionary<object, object>>();
|
||||
itemsMock
|
||||
.SetupSet(i => i[It.IsAny<object>()] = It.IsAny<object>())
|
||||
.Callback<object, object>((key, val) => itemsCallback.Add(key, val));
|
||||
|
||||
var configurationMock = new Mock<IConfiguration>();
|
||||
var children = new List<IConfigurationSection>();
|
||||
foreach (string ipAddress in restrictedIpsConfig)
|
||||
{
|
||||
var csm = new Mock<IConfigurationSection>();
|
||||
csm.Setup(cs => cs.Value).Returns(ipAddress);
|
||||
|
||||
children.Add(csm.Object);
|
||||
}
|
||||
|
||||
var configSectionMock = new Mock<IConfigurationSection>();
|
||||
configSectionMock
|
||||
.Setup(cs => cs.GetChildren())
|
||||
.Returns(children);
|
||||
|
||||
configurationMock
|
||||
.Setup(c => c.GetSection(configKey))
|
||||
.Returns(configExists ? configSectionMock.Object : null);
|
||||
|
||||
var requestServicesMock = new Mock<IServiceProvider>();
|
||||
requestServicesMock
|
||||
.Setup(s => s.GetService(typeof(IConfiguration)))
|
||||
.Returns(configurationMock.Object);
|
||||
|
||||
var contextMock = new Mock<HttpContext>();
|
||||
contextMock
|
||||
.Setup(c => c.Request)
|
||||
.Returns(requestMock.Object);
|
||||
contextMock
|
||||
.Setup(c => c.RequestServices)
|
||||
.Returns(requestServicesMock.Object);
|
||||
contextMock
|
||||
.Setup(c => c.Items)
|
||||
.Returns(itemsMock.Object);
|
||||
contextMock
|
||||
.Setup(c => c.Connection)
|
||||
.Returns(connectionInfoMock.Object);
|
||||
|
||||
var routeDataMock = new Mock<RouteData>();
|
||||
var actionDescriptorMock = new Mock<ActionDescriptor>();
|
||||
|
||||
return new ActionExecutingContext(new ActionContext
|
||||
{
|
||||
HttpContext = contextMock.Object,
|
||||
RouteData = routeDataMock.Object,
|
||||
ActionDescriptor = actionDescriptorMock.Object,
|
||||
}, new List<IFilterMetadata>(), new Dictionary<string, object>(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
341
UnitTests/AspNetCore/Attributes/IPWhitelistAttributeTests.cs
Normal file
341
UnitTests/AspNetCore/Attributes/IPWhitelistAttributeTests.cs
Normal file
@@ -0,0 +1,341 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
|
||||
namespace UnitTests.AspNetCore.Attributes
|
||||
{
|
||||
[TestClass]
|
||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||
public class IPWhitelistAttributeTests
|
||||
{
|
||||
private Dictionary<string, string> requestHeaders;
|
||||
private Dictionary<object, object> itemsCallback;
|
||||
private string configKey;
|
||||
private bool configExists;
|
||||
private List<string> allowedIpsConfig;
|
||||
|
||||
[TestInitialize]
|
||||
public void InitializeTest()
|
||||
{
|
||||
requestHeaders = new Dictionary<string, string>();
|
||||
itemsCallback = new Dictionary<object, object>();
|
||||
configKey = null;
|
||||
configExists = false;
|
||||
allowedIpsConfig = new List<string>();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDenyOnNoConfiguration()
|
||||
{
|
||||
// arrange
|
||||
var remote = IPAddress.Parse("192.168.178.1");
|
||||
var attribute = new IPWhitelistAttribute();
|
||||
var context = GetContext(remote);
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDenyOnWrongConfiguration()
|
||||
{
|
||||
// arrange
|
||||
var remote = IPAddress.Parse("192.168.178.1");
|
||||
var attribute = new IPWhitelistAttribute
|
||||
{
|
||||
AllowedIpAddresses = "192.168.178:1"
|
||||
};
|
||||
var context = GetContext(remote);
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldAllowLocalAccess()
|
||||
{
|
||||
// arrange
|
||||
var attribute = new IPWhitelistAttribute();
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNull(context.Result);
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDenyLocalAccess()
|
||||
{
|
||||
// arrange
|
||||
var attribute = new IPWhitelistAttribute
|
||||
{
|
||||
AllowLocalAccess = false
|
||||
};
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("192.168.178.10")]
|
||||
[DataRow("192.168.178.20")]
|
||||
public void ShouldAllowSpecificAddress(string address)
|
||||
{
|
||||
// arrange
|
||||
var remote = IPAddress.Parse(address);
|
||||
var attribute = new IPWhitelistAttribute
|
||||
{
|
||||
AllowLocalAccess = false,
|
||||
AllowedIpAddresses = ",127.0.0.0/8,192.168.178.10"
|
||||
};
|
||||
var context = GetContext(remote);
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
if (address == "192.168.178.10")
|
||||
{
|
||||
Assert.IsNull(context.Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldAllowLocalAccessConfig()
|
||||
{
|
||||
// arrange
|
||||
configKey = "White:List";
|
||||
configExists = true;
|
||||
allowedIpsConfig.Add("127.0.0.0/8");
|
||||
allowedIpsConfig.Add("192.168.178.10");
|
||||
var attribute = new IPWhitelistAttribute
|
||||
{
|
||||
AllowLocalAccess = true,
|
||||
ConfigurationKey = configKey
|
||||
};
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNull(context.Result);
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDenyLocalAccessConfig()
|
||||
{
|
||||
// arrange
|
||||
configKey = "White:List";
|
||||
configExists = true;
|
||||
allowedIpsConfig.Add("");
|
||||
allowedIpsConfig.Add("192.168.178.10");
|
||||
var attribute = new IPWhitelistAttribute
|
||||
{
|
||||
AllowLocalAccess = false,
|
||||
ConfigurationKey = configKey
|
||||
};
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("192.168.178.10")]
|
||||
[DataRow("192.168.178.20")]
|
||||
public void ShouldAllowSpecificAddressConfig(string address)
|
||||
{
|
||||
// arrange
|
||||
configKey = "White:List";
|
||||
configExists = true;
|
||||
allowedIpsConfig.Add("192.168.178.10");
|
||||
var attribute = new IPWhitelistAttribute
|
||||
{
|
||||
AllowLocalAccess = false,
|
||||
ConfigurationKey = configKey
|
||||
};
|
||||
var remote = IPAddress.Parse(address);
|
||||
var context = GetContext(remote);
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
if (address == "192.168.178.10")
|
||||
{
|
||||
Assert.IsNull(context.Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(remote, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDenyOnMissingConfiguration()
|
||||
{
|
||||
// arrange
|
||||
configKey = "White:List";
|
||||
configExists = false;
|
||||
var attribute = new IPWhitelistAttribute
|
||||
{
|
||||
AllowLocalAccess = false,
|
||||
ConfigurationKey = configKey
|
||||
};
|
||||
var context = GetContext();
|
||||
|
||||
// act
|
||||
attribute.OnActionExecuting(context);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(context.Result);
|
||||
Assert.IsTrue(context.Result is StatusCodeResult);
|
||||
Assert.AreEqual(403, ((StatusCodeResult)context.Result).StatusCode);
|
||||
|
||||
Assert.AreEqual(1, itemsCallback.Count);
|
||||
Assert.AreEqual(IPAddress.Loopback, itemsCallback["RemoteAddress"]);
|
||||
}
|
||||
|
||||
private ActionExecutingContext GetContext(IPAddress remote = null)
|
||||
{
|
||||
var requestHeaderMock = new Mock<IHeaderDictionary>();
|
||||
foreach (var header in requestHeaders)
|
||||
{
|
||||
requestHeaderMock
|
||||
.Setup(h => h.ContainsKey(header.Key))
|
||||
.Returns(true);
|
||||
requestHeaderMock
|
||||
.Setup(h => h[header.Key])
|
||||
.Returns(header.Value);
|
||||
}
|
||||
|
||||
var requestMock = new Mock<HttpRequest>();
|
||||
requestMock
|
||||
.Setup(r => r.Headers)
|
||||
.Returns(requestHeaderMock.Object);
|
||||
|
||||
var connectionInfoMock = new Mock<ConnectionInfo>();
|
||||
connectionInfoMock
|
||||
.Setup(ci => ci.LocalIpAddress)
|
||||
.Returns(IPAddress.Loopback);
|
||||
connectionInfoMock
|
||||
.Setup(ci => ci.RemoteIpAddress)
|
||||
.Returns(remote ?? IPAddress.Loopback);
|
||||
|
||||
var itemsMock = new Mock<IDictionary<object, object>>();
|
||||
itemsMock
|
||||
.SetupSet(i => i[It.IsAny<object>()] = It.IsAny<object>())
|
||||
.Callback<object, object>((key, val) => itemsCallback.Add(key, val));
|
||||
|
||||
var configurationMock = new Mock<IConfiguration>();
|
||||
var children = new List<IConfigurationSection>();
|
||||
foreach (string ipAddress in allowedIpsConfig)
|
||||
{
|
||||
var csm = new Mock<IConfigurationSection>();
|
||||
csm.Setup(cs => cs.Value).Returns(ipAddress);
|
||||
|
||||
children.Add(csm.Object);
|
||||
}
|
||||
|
||||
var configSectionMock = new Mock<IConfigurationSection>();
|
||||
configSectionMock
|
||||
.Setup(cs => cs.GetChildren())
|
||||
.Returns(children);
|
||||
|
||||
configurationMock
|
||||
.Setup(c => c.GetSection(configKey))
|
||||
.Returns(configExists ? configSectionMock.Object : null);
|
||||
|
||||
var requestServicesMock = new Mock<IServiceProvider>();
|
||||
requestServicesMock
|
||||
.Setup(s => s.GetService(typeof(IConfiguration)))
|
||||
.Returns(configurationMock.Object);
|
||||
|
||||
var contextMock = new Mock<HttpContext>();
|
||||
contextMock
|
||||
.Setup(c => c.Request)
|
||||
.Returns(requestMock.Object);
|
||||
contextMock
|
||||
.Setup(c => c.RequestServices)
|
||||
.Returns(requestServicesMock.Object);
|
||||
contextMock
|
||||
.Setup(c => c.Items)
|
||||
.Returns(itemsMock.Object);
|
||||
contextMock
|
||||
.Setup(c => c.Connection)
|
||||
.Returns(connectionInfoMock.Object);
|
||||
|
||||
var routeDataMock = new Mock<RouteData>();
|
||||
var actionDescriptorMock = new Mock<ActionDescriptor>();
|
||||
|
||||
return new ActionExecutingContext(new ActionContext
|
||||
{
|
||||
HttpContext = contextMock.Object,
|
||||
RouteData = routeDataMock.Object,
|
||||
ActionDescriptor = actionDescriptorMock.Object,
|
||||
}, new List<IFilterMetadata>(), new Dictionary<string, object>(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user