Resolve all 46 low-severity architecture review findings
This commit is contained in:
@@ -1,3 +1,9 @@
|
||||
using NSubstitute;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ZB.MOM.WW.Auth.Abstractions.Ldap;
|
||||
using ZB.MOM.WW.Auth.Abstractions.Roles;
|
||||
using ZB.MOM.WW.ScadaBridge.Security;
|
||||
using Microsoft.AspNetCore.Antiforgery;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@@ -67,16 +73,51 @@ public class AuthEndpointsCsrfTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PostAuthLogin_StillDisablesAntiforgery_PreAuthIsAcceptable()
|
||||
public async Task PostAuthLogin_ValidTokenAuthenticatesAndSetsCookie()
|
||||
{
|
||||
// Login is a pre-auth endpoint; disabling antiforgery there is acceptable
|
||||
// and intentional. This pins that the fix did not over-correct.
|
||||
var endpoints = BuildEndpoints();
|
||||
var login = Find(endpoints, "/auth/login", "POST");
|
||||
var endpoint = Find(BuildEndpoints(), "/auth/login", "POST")!;
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddAntiforgery();
|
||||
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
|
||||
services.AddSingleton(new LoginThrottle(TimeProvider.System, Options.Create(new SecurityOptions())));
|
||||
var ldap = Substitute.For<ILdapAuthService>();
|
||||
ldap.AuthenticateAsync("alice", "password", Arg.Any<CancellationToken>())
|
||||
.Returns(LdapAuthResult.Success("alice", "Alice", []));
|
||||
services.AddSingleton(ldap);
|
||||
var mapper = Substitute.For<IGroupRoleMapper<string>>();
|
||||
mapper.MapAsync(Arg.Any<IReadOnlyList<string>>(), Arg.Any<CancellationToken>())
|
||||
.Returns(new GroupRoleMapping<string>([Roles.Viewer], Scope: null));
|
||||
services.AddSingleton(mapper);
|
||||
using var provider = services.BuildServiceProvider();
|
||||
var tokenContext = new DefaultHttpContext { RequestServices = provider };
|
||||
var tokens = provider.GetRequiredService<IAntiforgery>().GetAndStoreTokens(tokenContext);
|
||||
var cookie = tokenContext.Response.Headers.SetCookie.Single()!.Split(';')[0];
|
||||
var context = new DefaultHttpContext { RequestServices = provider };
|
||||
context.Request.Headers.Cookie = cookie;
|
||||
context.Request.Method = "POST";
|
||||
context.Request.ContentType = "application/x-www-form-urlencoded";
|
||||
var form = $"username=alice&password=password&{tokens.FormFieldName}={Uri.EscapeDataString(tokens.RequestToken!)}";
|
||||
context.Request.Body = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(form));
|
||||
await endpoint.RequestDelegate!(context);
|
||||
Assert.Equal(StatusCodes.Status302Found, context.Response.StatusCode);
|
||||
Assert.Equal("/", context.Response.Headers.Location.ToString());
|
||||
Assert.Contains(context.Response.Headers.SetCookie, value => value!.StartsWith(".AspNetCore.Cookies="));
|
||||
}
|
||||
|
||||
Assert.NotNull(login);
|
||||
var antiforgery = login!.Metadata.GetMetadata<IAntiforgeryMetadata>();
|
||||
Assert.NotNull(antiforgery);
|
||||
Assert.False(antiforgery!.RequiresValidation);
|
||||
[Fact]
|
||||
public async Task PostAuthLogin_MissingTokenRejectsBeforeCredentialLookup()
|
||||
{
|
||||
var endpoint = Find(BuildEndpoints(), "/auth/login", "POST")!;
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddAntiforgery();
|
||||
using var provider = services.BuildServiceProvider();
|
||||
var context = new DefaultHttpContext { RequestServices = provider };
|
||||
context.Request.Method = "POST";
|
||||
context.Request.ContentType = "application/x-www-form-urlencoded";
|
||||
context.Request.Body = new MemoryStream(System.Text.Encoding.UTF8.GetBytes("username=test&password=password"));
|
||||
await endpoint.RequestDelegate!(context);
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user