refactor: rename ScadaLink → ZB.MOM.WW.ScadaBridge (code + projects + namespaces)
Solution + 23 src projects + 26 test projects renamed; folders, csproj, namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated. ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated. SQL roles/logins, LDAP domains, CLI command name, and CLI config dir (~/.scadalink → ~/.scadabridge) also renamed. Build green; 5 Host.Tests fail awaiting SQL login rename in next commit. Pre-existing StaleTagMonitor timing flakes unchanged. Rename script committed at tools/rename-to-scadabridge.sh.
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
using System.Net;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Auth;
|
||||
using ZB.MOM.WW.ScadaBridge.Security;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// WP-22: Auth flow integration tests.
|
||||
/// Tests that require a running LDAP server are marked with Integration trait.
|
||||
/// </summary>
|
||||
public class AuthFlowTests : IClassFixture<ScadaBridgeWebApplicationFactory>
|
||||
{
|
||||
private readonly ScadaBridgeWebApplicationFactory _factory;
|
||||
|
||||
public AuthFlowTests(ScadaBridgeWebApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoginEndpoint_WithEmptyCredentials_RedirectsToLoginWithError()
|
||||
{
|
||||
var client = _factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
|
||||
{
|
||||
AllowAutoRedirect = false
|
||||
});
|
||||
|
||||
var content = new FormUrlEncodedContent(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("username", ""),
|
||||
new KeyValuePair<string, string>("password", "")
|
||||
});
|
||||
|
||||
var response = await client.PostAsync("/auth/login", content);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||
var location = response.Headers.Location?.ToString() ?? "";
|
||||
Assert.Contains("/login", location);
|
||||
Assert.Contains("error", location, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LogoutEndpoint_ClearsCookieAndRedirects()
|
||||
{
|
||||
var client = _factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
|
||||
{
|
||||
AllowAutoRedirect = false
|
||||
});
|
||||
|
||||
var response = await client.PostAsync("/auth/logout", null);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||
var location = response.Headers.Location?.ToString() ?? "";
|
||||
Assert.Contains("/login", location);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JwtTokenService_GenerateAndValidate_RoundTrips()
|
||||
{
|
||||
using var scope = _factory.Services.CreateScope();
|
||||
var jwtService = scope.ServiceProvider.GetRequiredService<JwtTokenService>();
|
||||
|
||||
var token = jwtService.GenerateToken(
|
||||
displayName: "Test User",
|
||||
username: "testuser",
|
||||
roles: new[] { "Admin", "Design" },
|
||||
permittedSiteIds: null);
|
||||
|
||||
Assert.NotNull(token);
|
||||
|
||||
var principal = jwtService.ValidateToken(token);
|
||||
Assert.NotNull(principal);
|
||||
|
||||
var displayName = principal!.FindFirst(JwtTokenService.DisplayNameClaimType)?.Value;
|
||||
var username = principal.FindFirst(JwtTokenService.UsernameClaimType)?.Value;
|
||||
var roles = principal.FindAll(JwtTokenService.RoleClaimType).Select(c => c.Value).ToList();
|
||||
|
||||
Assert.Equal("Test User", displayName);
|
||||
Assert.Equal("testuser", username);
|
||||
Assert.Contains("Admin", roles);
|
||||
Assert.Contains("Design", roles);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JwtTokenService_WithSiteScopes_IncludesSiteIdClaims()
|
||||
{
|
||||
using var scope = _factory.Services.CreateScope();
|
||||
var jwtService = scope.ServiceProvider.GetRequiredService<JwtTokenService>();
|
||||
|
||||
var token = jwtService.GenerateToken(
|
||||
displayName: "Deployer",
|
||||
username: "deployer1",
|
||||
roles: new[] { "Deployment" },
|
||||
permittedSiteIds: new[] { "1", "3" });
|
||||
|
||||
var principal = jwtService.ValidateToken(token);
|
||||
Assert.NotNull(principal);
|
||||
|
||||
var siteIds = principal!.FindAll(JwtTokenService.SiteIdClaimType).Select(c => c.Value).ToList();
|
||||
Assert.Contains("1", siteIds);
|
||||
Assert.Contains("3", siteIds);
|
||||
}
|
||||
|
||||
[Trait("Category", "Integration")]
|
||||
[Fact]
|
||||
public async Task LoginEndpoint_WithValidLdapCredentials_SetsCookieAndRedirects()
|
||||
{
|
||||
// Requires GLAuth test LDAP server: docker compose -f infra/docker-compose.yml up -d glauth
|
||||
// GLAuth runs on localhost:3893, baseDN dc=scadabridge,dc=local, all passwords "password"
|
||||
if (!await IsLdapAvailableAsync())
|
||||
{
|
||||
// Skip gracefully if GLAuth not running — not a test failure
|
||||
return;
|
||||
}
|
||||
|
||||
var client = _factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
|
||||
{
|
||||
AllowAutoRedirect = false
|
||||
});
|
||||
|
||||
var content = new FormUrlEncodedContent(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("username", "admin"),
|
||||
new KeyValuePair<string, string>("password", "password")
|
||||
});
|
||||
|
||||
var response = await client.PostAsync("/auth/login", content);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||
var location = response.Headers.Location?.ToString() ?? "";
|
||||
Assert.Equal("/", location);
|
||||
|
||||
// Verify auth cookie was set
|
||||
var setCookieHeader = response.Headers.GetValues("Set-Cookie").FirstOrDefault();
|
||||
Assert.NotNull(setCookieHeader);
|
||||
Assert.Contains("ZB.MOM.WW.ScadaBridge.Auth", setCookieHeader);
|
||||
}
|
||||
|
||||
private static async Task<bool> IsLdapAvailableAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var tcp = new System.Net.Sockets.TcpClient();
|
||||
await tcp.ConnectAsync("localhost", 3893).WaitAsync(TimeSpan.FromSeconds(2));
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user