feat(auth): cut ScadaBridge over to ZB.MOM.WW.Auth.Ldap; nest+rename Ldap config; roles+sitescope via IGroupRoleMapper (Task 1.2/1.4)
This commit is contained in:
@@ -5,6 +5,8 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ZB.MOM.WW.Auth.Abstractions.Ldap;
|
||||
using ZB.MOM.WW.Auth.Abstractions.Roles;
|
||||
using ZB.MOM.WW.ScadaBridge.Security;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Auth;
|
||||
@@ -31,20 +33,25 @@ public static class AuthEndpoints
|
||||
return;
|
||||
}
|
||||
|
||||
var ldapAuth = context.RequestServices.GetRequiredService<LdapAuthService>();
|
||||
var ldapAuth = context.RequestServices.GetRequiredService<ILdapAuthService>();
|
||||
var jwtService = context.RequestServices.GetRequiredService<JwtTokenService>();
|
||||
var roleMapper = context.RequestServices.GetRequiredService<RoleMapper>();
|
||||
var roleMapper = context.RequestServices.GetRequiredService<IGroupRoleMapper<string>>();
|
||||
|
||||
var authResult = await ldapAuth.AuthenticateAsync(username, password);
|
||||
if (!authResult.Success)
|
||||
var authResult = await ldapAuth.AuthenticateAsync(username, password, context.RequestAborted);
|
||||
if (!authResult.Succeeded)
|
||||
{
|
||||
var errorMsg = Uri.EscapeDataString(authResult.ErrorMessage ?? "Authentication failed.");
|
||||
var errorMsg = Uri.EscapeDataString(LdapAuthFailureMessages.ToMessage(authResult.Failure));
|
||||
context.Response.Redirect($"/login?error={errorMsg}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Map LDAP groups to roles
|
||||
var roleMappingResult = await roleMapper.MapGroupsToRolesAsync(authResult.Groups ?? []);
|
||||
// Map LDAP groups to roles via the shared IGroupRoleMapper<string> seam
|
||||
// (Task 1.1 ScadaBridgeGroupRoleMapper, wrapping the DB-backed RoleMapper).
|
||||
// The full RoleMappingResult — including PermittedSiteIds and the
|
||||
// system-wide flag — is carried in the mapping's opaque Scope so the
|
||||
// site-scope→SiteId claims below are built exactly as before.
|
||||
var roleMapping = await roleMapper.MapAsync(authResult.Groups, context.RequestAborted);
|
||||
var scope = (RoleMappingResult)roleMapping.Scope!;
|
||||
|
||||
// Build claims from LDAP auth + role mapping.
|
||||
// CentralUI-005: no fixed "expires_at" absolute-cap claim is stamped
|
||||
@@ -52,21 +59,23 @@ public static class AuthEndpoints
|
||||
// (ZB.MOM.WW.ScadaBridge.Security AddCookie: ExpireTimeSpan = idle timeout,
|
||||
// SlidingExpiration = true). A frozen absolute claim would contradict
|
||||
// the documented sliding-refresh policy.
|
||||
var displayName = string.IsNullOrEmpty(authResult.DisplayName) ? username : authResult.DisplayName;
|
||||
var resolvedUsername = string.IsNullOrEmpty(authResult.Username) ? username : authResult.Username;
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new(ClaimTypes.Name, authResult.Username ?? username),
|
||||
new(JwtTokenService.DisplayNameClaimType, authResult.DisplayName ?? username),
|
||||
new(JwtTokenService.UsernameClaimType, authResult.Username ?? username),
|
||||
new(ClaimTypes.Name, resolvedUsername),
|
||||
new(JwtTokenService.DisplayNameClaimType, displayName),
|
||||
new(JwtTokenService.UsernameClaimType, resolvedUsername),
|
||||
};
|
||||
|
||||
foreach (var role in roleMappingResult.Roles)
|
||||
foreach (var role in roleMapping.Roles)
|
||||
{
|
||||
claims.Add(new Claim(JwtTokenService.RoleClaimType, role));
|
||||
}
|
||||
|
||||
if (!roleMappingResult.IsSystemWideDeployment)
|
||||
if (!scope.IsSystemWideDeployment)
|
||||
{
|
||||
foreach (var siteId in roleMappingResult.PermittedSiteIds)
|
||||
foreach (var siteId in scope.PermittedSiteIds)
|
||||
{
|
||||
claims.Add(new Claim(JwtTokenService.SiteIdClaimType, siteId));
|
||||
}
|
||||
@@ -94,33 +103,37 @@ public static class AuthEndpoints
|
||||
return Results.Json(new { error = "Username and password are required." }, statusCode: 400);
|
||||
}
|
||||
|
||||
var ldapAuth = context.RequestServices.GetRequiredService<LdapAuthService>();
|
||||
var ldapAuth = context.RequestServices.GetRequiredService<ILdapAuthService>();
|
||||
var jwtService = context.RequestServices.GetRequiredService<JwtTokenService>();
|
||||
var roleMapper = context.RequestServices.GetRequiredService<RoleMapper>();
|
||||
var roleMapper = context.RequestServices.GetRequiredService<IGroupRoleMapper<string>>();
|
||||
|
||||
var authResult = await ldapAuth.AuthenticateAsync(username, password);
|
||||
if (!authResult.Success)
|
||||
var authResult = await ldapAuth.AuthenticateAsync(username, password, context.RequestAborted);
|
||||
if (!authResult.Succeeded)
|
||||
{
|
||||
return Results.Json(
|
||||
new { error = authResult.ErrorMessage ?? "Authentication failed." },
|
||||
new { error = LdapAuthFailureMessages.ToMessage(authResult.Failure) },
|
||||
statusCode: 401);
|
||||
}
|
||||
|
||||
var roleMappingResult = await roleMapper.MapGroupsToRolesAsync(authResult.Groups ?? []);
|
||||
var roleMapping = await roleMapper.MapAsync(authResult.Groups, context.RequestAborted);
|
||||
var scope = (RoleMappingResult)roleMapping.Scope!;
|
||||
|
||||
var displayName = string.IsNullOrEmpty(authResult.DisplayName) ? username : authResult.DisplayName;
|
||||
var resolvedUsername = string.IsNullOrEmpty(authResult.Username) ? username : authResult.Username;
|
||||
|
||||
var token = jwtService.GenerateToken(
|
||||
authResult.DisplayName ?? username,
|
||||
authResult.Username ?? username,
|
||||
roleMappingResult.Roles,
|
||||
roleMappingResult.IsSystemWideDeployment ? null : roleMappingResult.PermittedSiteIds);
|
||||
displayName,
|
||||
resolvedUsername,
|
||||
roleMapping.Roles,
|
||||
scope.IsSystemWideDeployment ? null : scope.PermittedSiteIds);
|
||||
|
||||
return Results.Json(new
|
||||
{
|
||||
access_token = token,
|
||||
token_type = "Bearer",
|
||||
username = authResult.Username ?? username,
|
||||
display_name = authResult.DisplayName ?? username,
|
||||
roles = roleMappingResult.Roles,
|
||||
username = resolvedUsername,
|
||||
display_name = displayName,
|
||||
roles = roleMapping.Roles,
|
||||
});
|
||||
}).DisableAntiforgery();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user