using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.Server.Security;
///
/// Server-layer write-authorization policy. ACL enforcement lives here — drivers report
/// as discovery metadata only; the server decides
/// whether a given session is allowed to write a given attribute by checking the session's
/// roles (resolved at login via ) against the required
/// role for the attribute's classification.
///
///
/// Matches the table in docs/Configuration.md:
///
/// - FreeAccess: no role required — anonymous sessions can write (matches v1 default).
/// - Operate / SecuredWrite: WriteOperate role required.
/// - Tune: WriteTune role required.
/// - VerifiedWrite / Configure: WriteConfigure role required.
/// - ViewOnly: no role grants write access.
///
/// AlarmAck is checked at the alarm-acknowledge path, not here.
///
public static class WriteAuthzPolicy
{
public const string RoleWriteOperate = "WriteOperate";
public const string RoleWriteTune = "WriteTune";
public const string RoleWriteConfigure = "WriteConfigure";
///
/// Decide whether a session with is allowed to write to an
/// attribute with the given . Returns true for
/// FreeAccess regardless of roles (including empty / anonymous sessions) and
/// false for ViewOnly regardless of roles. Every other classification requires
/// the session to carry the mapped role — case-insensitive match.
///
public static bool IsAllowed(SecurityClassification classification, IReadOnlyCollection userRoles)
{
if (classification == SecurityClassification.FreeAccess) return true;
if (classification == SecurityClassification.ViewOnly) return false;
var required = RequiredRole(classification);
if (required is null) return false;
foreach (var r in userRoles)
{
if (string.Equals(r, required, StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}
///
/// Required role for a classification, or null when no role grants access
/// () or no role is needed
/// ( — also returns null; callers use
/// which handles the special-cases rather than branching on
/// null themselves).
///
public static string? RequiredRole(SecurityClassification classification) => classification switch
{
SecurityClassification.FreeAccess => null, // IsAllowed short-circuits
SecurityClassification.Operate => RoleWriteOperate,
SecurityClassification.SecuredWrite => RoleWriteOperate,
SecurityClassification.Tune => RoleWriteTune,
SecurityClassification.VerifiedWrite => RoleWriteConfigure,
SecurityClassification.Configure => RoleWriteConfigure,
SecurityClassification.ViewOnly => null, // IsAllowed short-circuits
_ => null,
};
}