Files
ScadaBridge/src/ZB.MOM.WW.ScadaBridge.Security/LdapAuthFailureMessages.cs
T

88 lines
4.7 KiB
C#

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,
};
}