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:
@@ -0,0 +1,87 @@
|
||||
using ZB.MOM.WW.Auth.Abstractions.Ldap;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Translates the shared <see cref="LdapAuthFailure"/> enum returned by
|
||||
/// <c>ZB.MOM.WW.Auth.Ldap</c>'s <see cref="ILdapAuthService"/> into the
|
||||
/// user-facing error strings ScadaBridge surfaced from its bespoke
|
||||
/// <c>LdapAuthService</c> before the Task 1.2 cutover.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The cutover replaced ScadaBridge's hand-rolled LDAP client (which returned a
|
||||
/// pre-formatted <c>ErrorMessage</c> string) with the shared library service
|
||||
/// (which returns a structured <see cref="LdapAuthFailure"/> code). This single
|
||||
/// adapter keeps the externally observable error text stable across the login
|
||||
/// (<c>/auth/login</c>, <c>/auth/token</c>) and Basic-Auth (ManagementService)
|
||||
/// surfaces so the user-visible behaviour does not regress.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Message-mapping rationale, preserving the donor's deliberate security framing:
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="LdapAuthFailure.BadCredentials"/> and
|
||||
/// <see cref="LdapAuthFailure.UserNotFound"/> both map to the same generic
|
||||
/// "Invalid username or password." — a username-enumeration guard
|
||||
/// (Security): a "user not found" message must be indistinguishable from a
|
||||
/// "wrong password" message.</item>
|
||||
/// <item><see cref="LdapAuthFailure.AmbiguousUser"/> (the directory returned
|
||||
/// two or more entries for the username) is a directory-data fault, not a
|
||||
/// user-credential one. The donor never attempted an ambiguous bind; the
|
||||
/// library rejects it outright. Surfaced as the misconfiguration message so
|
||||
/// the operator — not the user — is pointed at the cause.</item>
|
||||
/// <item><see cref="LdapAuthFailure.ServiceAccountBindFailed"/> keeps the
|
||||
/// donor's distinct "service is misconfigured" wording (Security-019) so a
|
||||
/// system-side fault is not blamed on user input. NOTE: the library also maps
|
||||
/// connect/search infrastructure failures (directory unreachable) into this
|
||||
/// bucket, so this message now covers "directory unavailable at connect/search
|
||||
/// time" as well — see <see cref="LdapAuthFailure.GroupLookupFailed"/> for the
|
||||
/// post-bind directory-outage case.</item>
|
||||
/// <item><see cref="LdapAuthFailure.GroupLookupFailed"/> keeps the donor's
|
||||
/// "directory is temporarily unavailable" wording (Security-012): a post-bind
|
||||
/// group-lookup failure means the directory is partially unavailable and the
|
||||
/// login is failed closed rather than admitting a roleless session. NOTE: the
|
||||
/// library additionally treats a successful-but-empty group set as
|
||||
/// <see cref="LdapAuthFailure.GroupLookupFailed"/>, whereas the donor admitted
|
||||
/// an empty-group user as a successful (roleless) login — a documented
|
||||
/// behavioural deviation of the cutover.</item>
|
||||
/// <item><see cref="LdapAuthFailure.Disabled"/> (the provider is turned off via
|
||||
/// <c>Enabled = false</c>) maps to a neutral "not available" message.</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static class LdapAuthFailureMessages
|
||||
{
|
||||
/// <summary>The generic, enumeration-safe message for a bad-credentials / user-not-found failure.</summary>
|
||||
public const string InvalidCredentials = "Invalid username or password.";
|
||||
|
||||
/// <summary>The system-misconfiguration message (service-account bind / ambiguous user / unreachable directory).</summary>
|
||||
public const string Misconfigured = "Authentication service is misconfigured. Contact an administrator.";
|
||||
|
||||
/// <summary>The transient directory-outage message for a post-bind group-lookup failure.</summary>
|
||||
public const string DirectoryUnavailable = "The directory is temporarily unavailable. Please try again.";
|
||||
|
||||
/// <summary>The provider-disabled message.</summary>
|
||||
public const string Disabled = "Authentication is not available.";
|
||||
|
||||
/// <summary>The fallback message for an unrecognised failure code.</summary>
|
||||
public const string Generic = "Authentication failed.";
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="LdapAuthFailure"/> to its user-facing message. A
|
||||
/// <see langword="null"/> failure (which should not occur on a failed result)
|
||||
/// falls back to <see cref="Generic"/>.
|
||||
/// </summary>
|
||||
/// <param name="failure">The structured failure code from <see cref="LdapAuthResult.Failure"/>.</param>
|
||||
/// <returns>The user-facing error string to surface.</returns>
|
||||
public static string ToMessage(LdapAuthFailure? failure) => failure switch
|
||||
{
|
||||
LdapAuthFailure.BadCredentials => InvalidCredentials,
|
||||
LdapAuthFailure.UserNotFound => InvalidCredentials,
|
||||
LdapAuthFailure.AmbiguousUser => Misconfigured,
|
||||
LdapAuthFailure.ServiceAccountBindFailed => Misconfigured,
|
||||
LdapAuthFailure.GroupLookupFailed => DirectoryUnavailable,
|
||||
LdapAuthFailure.Disabled => Disabled,
|
||||
_ => Generic,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user