88 lines
4.7 KiB
C#
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,
|
|
};
|
|
}
|