mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-29 07:07:24 +08:00
# Description Adds the OPC UA NodeManagement service set (AddNodes / DeleteNodes / AddReferences / DeleteReferences) to the server SDK and fixes a long-standing shutdown bug where `NodeState.Delete` callbacks never fired on address-space teardown. ### NodeManagement service set (#3736) - New optional `INodeManagementAsyncNodeManager` facet interface (added to `INodeManager.cs` alongside other facets, discovered via `as`, **not** part of the `IAsyncNodeManager` composite). Opt-in per NodeManager via `AllowNodeManagement` — default is `false` so existing NodeManagers preserve binary compatibility. - Dispatcher in `MasterNodeManager` routes: - **AddNodes** by `RequestedNewNodeId.NamespaceIndex` when supplied, otherwise to the parent's owning NodeManager. When the parent's owner is read-only (e.g. `ObjectsFolder` owned by `CoreNodeManager`), the dispatcher falls back to the first opted-in NodeManager so conformance flows work out of the box. - **DeleteNodes** / **AddReferences** / **DeleteReferences** to the owning NodeManager of the target or source node, returning `BadUserAccessDenied` when that NodeManager has not opted in. Cross-NodeManager `AddReference` / `DeleteReference` best-effort mirrors the inverse edge on the target's NodeManager. - `AsyncCustomNodeManager` provides a default implementation: per-item validation, `RequestedNewNodeId` honored, browse-name dedup for both local and cross-NodeManager parents, `DeleteTargetReferences` semantics, and a NodeId allocator fallback when an `INodeIdFactory.New` override returns a null `NodeId` (e.g. parent-based hierarchical naming with no parent attached). - `StandardServer` adds the four service overrides plumbed through `OperationLimits.MaxNodesPerNodeManagement`, audit events, and `OnRequestComplete`; new `ReportAuditAddReferencesEvent` / `ReportAuditDeleteReferencesEvent` helpers in `AuditEvents`. - Quickstarts `ReferenceNodeManager` opts in via `AllowNodeManagement => true`; the legacy partial-class override in `ReferenceServer.NodeManagement.cs` is removed. ### `NodeState.Delete` on shutdown (#3762) - `CustomNodeManager.DeleteAddressSpace` and `AsyncCustomNodeManager.DeleteAddressSpaceAsync` now snapshot root `PredefinedNodes` (skipping children whose parent is in the snapshot to prevent double-fire), invoke `NodeState.Delete(SystemContext)` on each root (which recurses into children and raises `OnBeforeDelete` / `OnAfterDelete` / `NodeStateChangeMasks.Deleted`), and then clear the map. - Derived overrides in Quickstarts `ReferenceNodeManager`, `SampleNodeManager`, and `FileSystemNodeManager` chain to `base` so the callback fires for their nodes too. ### Tests - **21 new dispatcher unit tests** in `Tests/Opc.Ua.Server.Tests/NodeManager/AsyncCustomNodeManagerNodeManagementTests.cs` covering `AllowNodeManagement` default/opt-in, AddNode validation (`BadBrowseNameInvalid`, `BadParentNodeIdInvalid`, `BadNodeIdRejected`, `BadNodeIdExists`, `BadBrowseNameDuplicated`, `BadNodeClassInvalid`), RequestedNewNodeId paths, DeleteNode + `DeleteTargetReferences`, AddReference duplicate detection (`BadDuplicateReferenceNotAllowed`), and DeleteReference (`BadNoMatch`). - **2 new DeleteAddressSpaceAsync tests** in `AsyncCustomNodeManagerTests` verifying `NodeState.Delete` invocation and no double-fire on children. - Updated InformationModel `NodeManagementAddRefTests.AddReferenceBetweenExistingNodesSucceedsAsync` to accept `BadUserAccessDenied` / `BadNotSupported` when the source NM has opted out. - **Full `Opc.Ua.Server.Tests` on net10.0: 1709 passed, 0 failed.** - **All 26 InformationModel `Category=NodeManagement` conformance tests pass** against the reference server (1 skipped where source NM opts out). ### Docs - New [`Docs/NodeManagement.md`](Docs/NodeManagement.md) with architecture, opt-in pattern, dispatch and routing rules, request-handling table, error-code table, audit events, and custom-behavior examples. - `Docs/MigrationGuide.md` gets two new sections — *NodeManagement service set* (downstream migration steps) and *DeleteAddressSpace invokes NodeState.Delete (#3762)* (shutdown behavior change) — with TOC entries. - `Docs/README.md` links the new `NodeManagement.md`. ## Related Issues - Fixes #3736 - Fixes #3762 ## Checklist _Put an `x` in the boxes that apply. You can complete these step by step after opening the PR._ - [ ] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [x] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [x] I have added all necessary documentation. - [x] I have verified that my changes do not introduce (new) build or analyzer warnings. - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [ ] I have addressed **all** PR feedback received.
2234 lines
88 KiB
C#
2234 lines
88 KiB
C#
/* ========================================================================
|
|
* Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved.
|
|
*
|
|
* OPC Foundation MIT License 1.00
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use,
|
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following
|
|
* conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* The complete license agreement can be found here:
|
|
* http://opcfoundation.org/License/MIT/1.00/
|
|
* ======================================================================*/
|
|
|
|
using System;
|
|
using System.Globalization;
|
|
using Microsoft.Extensions.Logging;
|
|
using Opc.Ua.Security.Certificates;
|
|
|
|
namespace Opc.Ua.Server
|
|
{
|
|
/// <summary>
|
|
/// An interface to report audit events in the server.
|
|
/// </summary>
|
|
public interface IAuditEventServer
|
|
{
|
|
/// <summary>
|
|
/// If auditing is enabled.
|
|
/// </summary>
|
|
bool Auditing { get; }
|
|
|
|
/// <summary>
|
|
/// The default system context for the audit events.
|
|
/// </summary>
|
|
ISystemContext DefaultAuditContext { get; }
|
|
|
|
/// <summary>
|
|
/// Called by any component to report an audit event.
|
|
/// </summary>
|
|
/// <param name="context">The context.</param>
|
|
/// <param name="e">The event.</param>
|
|
void ReportAuditEvent(ISystemContext context, AuditEventState e);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The implementation of audit events.
|
|
/// </summary>
|
|
public static class AuditEvents
|
|
{
|
|
/// <summary>
|
|
/// Placeholder used in audit event <c>InputArguments</c> to indicate
|
|
/// a private key byte string has been redacted before the event was
|
|
/// raised. Used by <c>UpdateCertificate</c> per OPC 10000-12 §7.10.3
|
|
/// to avoid leaking private-key material into audit payloads.
|
|
/// </summary>
|
|
public static readonly ByteString RedactedPrivateKey = ByteString.Empty;
|
|
|
|
/// <summary>
|
|
/// Report Audit event
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="operationContext">Client operation info</param>
|
|
/// <param name="methodName">Audit method name</param>
|
|
/// <param name="serviceResultException">The service exception that includes also a status code</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditEvent(
|
|
this IAuditEventServer? server,
|
|
OperationContext operationContext,
|
|
string methodName,
|
|
ServiceResultException serviceResultException,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ISystemContext systemContext = server.DefaultAuditContext;
|
|
|
|
var e = new AuditEventState(null);
|
|
|
|
var message = new TranslationInfo(
|
|
"AuditEvent",
|
|
"en-US",
|
|
$"Method {methodName} failed. Result: {serviceResultException.Message}.");
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
StatusCode.IsGood(serviceResultException.StatusCode),
|
|
DateTime.UtcNow
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, ObjectIds.Server, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
$"Attribute/{methodName}",
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientUserId,
|
|
operationContext?.UserIdentity?.DisplayName!,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientAuditEntryId,
|
|
operationContext?.AuditEntryId!,
|
|
false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an AuditWriteUpdate event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="writeValue">The value to write.</param>
|
|
/// <param name="oldValue">The old value of the node.</param>
|
|
/// <param name="statusCode">The resulted status code.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditWriteUpdateEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
WriteValue writeValue,
|
|
Variant oldValue,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
if (systemContext is not ISessionSystemContext session ||
|
|
session.UserIdentity == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var e = new AuditWriteUpdateEventState(null);
|
|
|
|
var message = new TranslationInfo(
|
|
"AuditWriteUpdateEvent",
|
|
"en-US",
|
|
"AuditWriteUpdateEvent.");
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
StatusCode.IsGood(statusCode),
|
|
DateTime.UtcNow
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, writeValue.NodeId, false);
|
|
e.SetChildValue(systemContext, BrowseNames.SourceName, "Attribute/Write", false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientUserId,
|
|
session.UserIdentity.DisplayName,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientAuditEntryId,
|
|
systemContext.AuditEntryId!,
|
|
false);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.AttributeId,
|
|
writeValue.AttributeId,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.IndexRange,
|
|
writeValue.IndexRange!,
|
|
false);
|
|
|
|
Variant newValue;
|
|
if (!writeValue.ParsedIndexRange.IsNull)
|
|
{
|
|
newValue = oldValue;
|
|
writeValue.ParsedIndexRange.UpdateRange(
|
|
ref newValue,
|
|
writeValue.Value.WrappedValue);
|
|
}
|
|
else
|
|
{
|
|
newValue = writeValue.Value.WrappedValue;
|
|
}
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.NewValue, newValue, false);
|
|
e.SetChildValue(systemContext, BrowseNames.OldValue, oldValue, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditWriteUpdateEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an AuditHistoryValueUpdate event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="updateDataDetails">Update data details</param>
|
|
/// <param name="oldValues">The old values</param>
|
|
/// <param name="statusCode">The resulting status code</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditHistoryValueUpdateEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
UpdateDataDetails updateDataDetails,
|
|
DataValue[] oldValues,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var e = new AuditHistoryValueUpdateEventState(null);
|
|
|
|
InitializeAuditHistoryUpdateEvent(
|
|
e,
|
|
systemContext,
|
|
"AuditHistoryValueUpdateEvent",
|
|
"Attribute/HistoryValueUpdate",
|
|
updateDataDetails,
|
|
statusCode);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.UpdatedNode,
|
|
updateDataDetails.NodeId,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.PerformInsertReplace,
|
|
updateDataDetails.PerformInsertReplace);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.NewValues,
|
|
updateDataDetails.UpdateValues,
|
|
false);
|
|
e.SetChildValue(systemContext, BrowseNames.OldValues, oldValues.ToArrayOf(), false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditHistoryValueUpdateEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an AuditHistoryValueUpdate event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="updateStructureDataDetails">Update structure data details</param>
|
|
/// <param name="oldValues">The old values</param>
|
|
/// <param name="statusCode">The resulting status code</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditHistoryAnnotationUpdateEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
UpdateStructureDataDetails updateStructureDataDetails,
|
|
ArrayOf<DataValue> oldValues,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var e = new AuditHistoryAnnotationUpdateEventState(null);
|
|
|
|
InitializeAuditHistoryUpdateEvent(
|
|
e,
|
|
systemContext,
|
|
"AuditHistoryAnnotationUpdateEvent",
|
|
"Attribute/HistoryAnnotationUpdate",
|
|
updateStructureDataDetails,
|
|
statusCode);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.PerformInsertReplace,
|
|
updateStructureDataDetails.PerformInsertReplace);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.NewValues,
|
|
updateStructureDataDetails.UpdateValues,
|
|
false);
|
|
e.SetChildValue(systemContext, BrowseNames.OldValues, oldValues, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditHistoryValueUpdateEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an AuditHistoryEventUpdate event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="updateEventDetails">Update event details</param>
|
|
/// <param name="oldValues">The old values</param>
|
|
/// <param name="statusCode">The resulting status code</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditHistoryEventUpdateEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
UpdateEventDetails updateEventDetails,
|
|
ArrayOf<HistoryEventFieldList> oldValues,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var e = new AuditHistoryEventUpdateEventState(null);
|
|
|
|
InitializeAuditHistoryUpdateEvent(
|
|
e,
|
|
systemContext,
|
|
"AuditHistoryEventUpdateEvent",
|
|
"Attribute/HistoryEventUpdate",
|
|
updateEventDetails,
|
|
statusCode);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.UpdatedNode,
|
|
updateEventDetails.NodeId,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.PerformInsertReplace,
|
|
updateEventDetails.PerformInsertReplace);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.Filter,
|
|
updateEventDetails.Filter,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.NewValues,
|
|
updateEventDetails.EventData,
|
|
false);
|
|
e.SetChildValue(systemContext, BrowseNames.OldValues, oldValues, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditHistoryEventUpdateEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an AuditHistoryRawModifyDelete event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="deleteRawModifiedDetails">History raw modified details</param>
|
|
/// <param name="oldValues">The old values</param>
|
|
/// <param name="statusCode">The resulting status code</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditHistoryRawModifyDeleteEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
DeleteRawModifiedDetails deleteRawModifiedDetails,
|
|
ArrayOf<DataValue> oldValues,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var e = new AuditHistoryRawModifyDeleteEventState(null);
|
|
|
|
InitializeAuditHistoryUpdateEvent(
|
|
e,
|
|
systemContext,
|
|
"AuditHistoryRawModifyDeleteEvent",
|
|
"Attribute/HistoryRawModifyDelete",
|
|
deleteRawModifiedDetails,
|
|
statusCode);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.UpdatedNode,
|
|
deleteRawModifiedDetails.NodeId,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.IsDeleteModified,
|
|
deleteRawModifiedDetails.IsDeleteModified,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.StartTime,
|
|
deleteRawModifiedDetails.StartTime,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.EndTime,
|
|
deleteRawModifiedDetails.EndTime,
|
|
false);
|
|
e.SetChildValue(systemContext, BrowseNames.OldValues, oldValues, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditHistoryRawModifyDeleteEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an AuditHistoryAtTimeDelete event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="deleteAtTimeDetails">History delete at time details</param>
|
|
/// <param name="oldValues">The old values</param>
|
|
/// <param name="statusCode">The resulting status code</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditHistoryAtTimeDeleteEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
DeleteAtTimeDetails deleteAtTimeDetails,
|
|
DataValue[] oldValues,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var e = new AuditHistoryAtTimeDeleteEventState(null);
|
|
|
|
InitializeAuditHistoryUpdateEvent(
|
|
e,
|
|
systemContext,
|
|
"AuditHistoryAtTimeDeleteEvent",
|
|
"Attribute/HistoryAtTimeDelete",
|
|
deleteAtTimeDetails,
|
|
statusCode);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.UpdatedNode,
|
|
deleteAtTimeDetails.NodeId,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ReqTimes,
|
|
deleteAtTimeDetails.ReqTimes,
|
|
false);
|
|
e.SetChildValue(systemContext, BrowseNames.OldValues, Variant.From(oldValues), false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditHistoryAtTimeDeleteEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an AuditHistoryEventDelete event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="deleteEventDetails">History delete event details</param>
|
|
/// <param name="oldValues">The old values</param>
|
|
/// <param name="statusCode">The resulting status code</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditHistoryEventDeleteEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
DeleteEventDetails deleteEventDetails,
|
|
DataValue[] oldValues,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var e = new AuditHistoryEventDeleteEventState(null);
|
|
|
|
InitializeAuditHistoryUpdateEvent(
|
|
e,
|
|
systemContext,
|
|
"AuditHistoryEventDeleteEvent",
|
|
"Attribute/HistoryEventDelete",
|
|
deleteEventDetails,
|
|
statusCode);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.UpdatedNode,
|
|
deleteEventDetails.NodeId,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.EventIds,
|
|
Variant.From(deleteEventDetails.EventIds),
|
|
false);
|
|
e.SetChildValue(systemContext, BrowseNames.OldValues, Variant.From(oldValues), false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditHistoryEventDeleteEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports all audit events for client certificate ServiceResultException.
|
|
/// It goes recursively for all service results stored in the exception
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="clientCertificate">The client certificate.</param>
|
|
/// <param name="exception">The Exception that triggers a certificate audit event.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditCertificateEvent(
|
|
this IAuditEventServer? server,
|
|
Certificate clientCertificate,
|
|
Exception? exception,
|
|
ILogger logger)
|
|
{
|
|
if (exception == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ISystemContext systemContext = server.DefaultAuditContext;
|
|
|
|
while (exception != null)
|
|
{
|
|
if (exception is ServiceResultException sre && sre.InnerResult != null)
|
|
{
|
|
// Each validation step has a unique error status and audit event type
|
|
// that shall be reported if the check fails.
|
|
server.ReportAuditCertificateEvent(logger, systemContext, clientCertificate, sre);
|
|
}
|
|
exception = exception.InnerException;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(
|
|
ex,
|
|
"Error while reporting ReportAuditCertificateDataMismatch event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Report the audit certificate event for the specified ServiceResultException
|
|
/// </summary>
|
|
private static void ReportAuditCertificateEvent(
|
|
this IAuditEventServer server,
|
|
ILogger logger,
|
|
ISystemContext systemContext,
|
|
Certificate clientCertificate,
|
|
ServiceResultException sre)
|
|
{
|
|
try
|
|
{
|
|
if (StatusCode.IsBad(sre!.InnerResult!.Code))
|
|
{
|
|
AuditCertificateEventState auditCertificateEventState;
|
|
if (sre.StatusCode == StatusCodes.BadCertificateTimeInvalid ||
|
|
sre.StatusCode == StatusCodes.BadCertificateIssuerTimeInvalid)
|
|
{
|
|
// create AuditCertificateExpiredEventType
|
|
auditCertificateEventState = new AuditCertificateExpiredEventState(
|
|
null);
|
|
}
|
|
else if (sre.StatusCode == StatusCodes.BadCertificateInvalid ||
|
|
sre.StatusCode == StatusCodes.BadCertificateChainIncomplete ||
|
|
sre.StatusCode == StatusCodes.BadCertificatePolicyCheckFailed)
|
|
{
|
|
// create AuditCertificateInvalidEventType
|
|
auditCertificateEventState = new AuditCertificateInvalidEventState(
|
|
null);
|
|
}
|
|
else if (sre.StatusCode == StatusCodes.BadCertificateUntrusted)
|
|
{
|
|
// create AuditCertificateUntrustedEventType
|
|
auditCertificateEventState = new AuditCertificateUntrustedEventState(
|
|
null);
|
|
}
|
|
else if (sre.StatusCode == StatusCodes.BadCertificateRevoked ||
|
|
sre.StatusCode == StatusCodes.BadCertificateIssuerRevoked ||
|
|
sre.StatusCode == StatusCodes.BadCertificateRevocationUnknown)
|
|
{
|
|
// create AuditCertificateRevokedEventType
|
|
auditCertificateEventState = new AuditCertificateRevokedEventState(
|
|
null);
|
|
}
|
|
else if (sre.StatusCode == StatusCodes.BadCertificateUseNotAllowed ||
|
|
sre.StatusCode == StatusCodes.BadCertificateIssuerUseNotAllowed)
|
|
{
|
|
// create AuditCertificateMismatchEventType
|
|
auditCertificateEventState = new AuditCertificateMismatchEventState(
|
|
null);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
auditCertificateEventState.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
LocalizedText.From(sre.Message),
|
|
false,
|
|
DateTime.UtcNow
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
auditCertificateEventState.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"Security/Certificate",
|
|
false);
|
|
|
|
// set AuditSecurityEventType fields
|
|
auditCertificateEventState.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.StatusCodeId,
|
|
sre.InnerResult.StatusCode,
|
|
false);
|
|
|
|
// set AuditCertificateEventType fields
|
|
auditCertificateEventState.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.Certificate,
|
|
clientCertificate?.RawData.ToByteString() ?? default,
|
|
false);
|
|
|
|
server.ReportAuditEvent(systemContext, auditCertificateEventState);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(
|
|
ex,
|
|
"Error while reporting ReportAuditCertificateDataMismatch event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports the AuditCertificateDataMismatchEventType for Invalid Uri
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="clientCertificate">The client certificate</param>
|
|
/// <param name="invalidHostName">The string that represents the host name passed in as
|
|
/// part of the URL that is found to be invalid. If the host name was not invalid it can be null.</param>
|
|
/// <param name="invalidUri">The URI that was passed in and found to not match what is
|
|
/// contained in the certificate. If the URI was not invalid it can be null.</param>
|
|
/// <param name="statusCode">The status code.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditCertificateDataMismatchEvent(
|
|
this IAuditEventServer? server,
|
|
Certificate clientCertificate,
|
|
string? invalidHostName,
|
|
string? invalidUri,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ISystemContext systemContext = server.DefaultAuditContext;
|
|
|
|
// create AuditCertificateDataMismatchEventType
|
|
var e = new AuditCertificateDataMismatchEventState(null);
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
default,
|
|
StatusCode.IsGood(statusCode),
|
|
DateTime.UtcNow
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"Security/Certificate",
|
|
false);
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, ObjectIds.Server, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
// set AuditSecurityEventType fields
|
|
e.SetChildValue(systemContext, BrowseNames.StatusCodeId, statusCode, false);
|
|
// set AuditCertificateEventType fields
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.Certificate,
|
|
Variant.From(ByteString.From(clientCertificate?.RawData)),
|
|
false);
|
|
// set AuditCertificateDataMismatchEventState fields
|
|
e.SetChildValue(systemContext, BrowseNames.InvalidUri, invalidUri!, false);
|
|
e.SetChildValue(systemContext, BrowseNames.InvalidHostname, invalidHostName!, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(
|
|
ex,
|
|
"Error while reporting ReportAuditCertificateDataMismatchEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Report the AuditCancelEventState
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="sessionId">Session id of the current session</param>
|
|
/// <param name="requestHandle">The handle of the canceled request</param>
|
|
/// <param name="statusCode">The resulted status code of cancel request.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditCancelEvent(
|
|
this IAuditEventServer? server,
|
|
NodeId sessionId,
|
|
uint requestHandle,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ISystemContext systemContext = server.DefaultAuditContext;
|
|
|
|
// create AuditCancelEventState
|
|
var e = new AuditCancelEventState(null);
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
LocalizedText.From($"Cancel requested for sessionId: {sessionId} with requestHandle: {requestHandle}"),
|
|
StatusCode.IsGood(statusCode),
|
|
DateTime.UtcNow
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceName, "Session/Cancel", false);
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, ObjectIds.Server, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
// set AuditSecurityEventType fields
|
|
e.SetChildValue(systemContext, BrowseNames.StatusCodeId, statusCode, false);
|
|
// set AuditSessionEventType fields
|
|
e.SetChildValue(systemContext, BrowseNames.SessionId, sessionId, false);
|
|
// set AuditCancelEventType fields
|
|
e.SetChildValue(systemContext, BrowseNames.RequestHandle, requestHandle, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting ReportAuditCancelEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports a RoleMappingRuleChangedAuditEvent when a method is called on a RoleType instance
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="roleStateObjectId">Role</param>
|
|
/// <param name="method">The method</param>
|
|
/// <param name="inputArguments">method arguments</param>
|
|
/// <param name="status">Status of call</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditRoleMappingRuleChangedEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
NodeId roleStateObjectId,
|
|
MethodState method,
|
|
ArrayOf<Variant> inputArguments,
|
|
bool status,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
// create RoleMappingRuleChangedAuditEventState
|
|
var e = new RoleMappingRuleChangedAuditEventState(null);
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
LocalizedText.From($"RoleMappingRuleChanged - {method?.BrowseName}"),
|
|
status,
|
|
DateTime.UtcNow
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceName, "Attribute/Call", false);
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, roleStateObjectId, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
// set AuditUpdateMethodEventType fields
|
|
e.SetChildValue(systemContext, BrowseNames.MethodId, method?.NodeId ?? default, false);
|
|
e.SetChildValue(systemContext, BrowseNames.InputArguments, inputArguments, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(
|
|
ex,
|
|
"Error while reporting ReportRoleMappingRuleChangedAuditEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an audit create session event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="auditEntryId">The audit entry id.</param>
|
|
/// <param name="session">The session object that was created.</param>
|
|
/// <param name="revisedSessionTimeout">The revised session timeout</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
/// <param name="exception">The exception received during create session request</param>
|
|
public static void ReportAuditCreateSessionEvent(
|
|
this IAuditEventServer? server,
|
|
string auditEntryId,
|
|
ISession session,
|
|
double revisedSessionTimeout,
|
|
ILogger logger,
|
|
Exception? exception = null)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ISystemContext systemContext = server.DefaultAuditContext;
|
|
|
|
// raise an audit event.
|
|
var e = new AuditCreateSessionEventState(null);
|
|
|
|
TranslationInfo message = default;
|
|
if (exception == null)
|
|
{
|
|
message = new TranslationInfo(
|
|
"AuditCreateSessionEvent",
|
|
"en-US",
|
|
$"Session with ID:{session?.Id} was created.");
|
|
}
|
|
else
|
|
{
|
|
message = new TranslationInfo(
|
|
"AuditCreateSessionEvent",
|
|
"en-US",
|
|
$"Error while creating session with ID:{session?.Id}: Exception: {exception.Message}.");
|
|
}
|
|
|
|
InitializeAuditSessionEvent(
|
|
systemContext,
|
|
e,
|
|
message,
|
|
exception == null,
|
|
session!,
|
|
auditEntryId);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientUserId,
|
|
"System/CreateSession",
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"Session/CreateSession",
|
|
false);
|
|
|
|
// set AuditCreateSessionEventState fields
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientCertificate,
|
|
ByteString.From(session?.ClientCertificate?.RawData),
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientCertificateThumbprint,
|
|
session?.ClientCertificate?.Thumbprint!,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.RevisedSessionTimeout,
|
|
revisedSessionTimeout,
|
|
false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(
|
|
ex,
|
|
"Error while reporting AuditCreateSessionEvent event for SessionId {SessionId}.",
|
|
session?.Id);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an audit activate session event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
/// <param name="auditEntryId">The audit entry id.</param>
|
|
/// <param name="session">The session that is activated.</param>
|
|
/// <param name="exception">The exception received during activate session request</param>
|
|
public static void ReportAuditActivateSessionEvent(
|
|
this IAuditEventServer? server,
|
|
ILogger logger,
|
|
string auditEntryId,
|
|
ISession session,
|
|
Exception? exception = null)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ISystemContext systemContext = server.DefaultAuditContext;
|
|
|
|
var e = new AuditActivateSessionEventState(null);
|
|
|
|
TranslationInfo message = default;
|
|
if (exception == null)
|
|
{
|
|
message = new TranslationInfo(
|
|
"AuditActivateSessionEvent",
|
|
"en-US",
|
|
$"Session with Id:{session?.Id} was activated.");
|
|
}
|
|
else
|
|
{
|
|
message = new TranslationInfo(
|
|
"AuditActivateSessionEvent",
|
|
"en-US",
|
|
$"Error while activate session with ID:{session?.Id}. Exception: {exception.Message}.");
|
|
}
|
|
|
|
InitializeAuditSessionEvent(
|
|
systemContext,
|
|
e,
|
|
message,
|
|
exception == null,
|
|
session!,
|
|
auditEntryId);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"Session/ActivateSession",
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.UserIdentityToken,
|
|
CoreUtils.Clone(session?.IdentityToken?.Token)!,
|
|
false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
logger.LogError(
|
|
e,
|
|
"Error while reporting AuditActivateSessionEvent event for SessionId {SessionId}.",
|
|
session?.Id);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an audit Url Mismatch event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="auditEntryId">The audit entry id.</param>
|
|
/// <param name="session">The session object that was created.</param>
|
|
/// <param name="revisedSessionTimeout">The revised session timeout</param>
|
|
/// <param name="endpointUrl">The invalid endpoint url</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditUrlMismatchEvent(
|
|
this IAuditEventServer? server,
|
|
string auditEntryId,
|
|
ISession session,
|
|
double revisedSessionTimeout,
|
|
string endpointUrl,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ISystemContext systemContext = server.DefaultAuditContext;
|
|
|
|
var e = new AuditUrlMismatchEventState(null);
|
|
|
|
var message = new TranslationInfo(
|
|
"AuditUrlMismatchEvent",
|
|
"en-US",
|
|
$"Session with ID:{session.Id} was created but the endpoint URL does not match the domain names in the server certificate.");
|
|
|
|
InitializeAuditSessionEvent(
|
|
systemContext,
|
|
e,
|
|
message,
|
|
false,
|
|
session,
|
|
auditEntryId);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientUserId,
|
|
"System/CreateSession",
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"Session/CreateSession",
|
|
false);
|
|
|
|
// set AuditCreateSessionEventState fields
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientCertificate,
|
|
ByteString.From(session?.ClientCertificate?.RawData),
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientCertificateThumbprint,
|
|
session?.ClientCertificate?.Thumbprint!,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.RevisedSessionTimeout,
|
|
revisedSessionTimeout,
|
|
false);
|
|
|
|
// set AuditUrlMismatchEventState
|
|
e.SetChildValue(systemContext, BrowseNames.EndpointUrl, endpointUrl, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
logger.LogError(
|
|
e,
|
|
"Error while reporting AuditUrlMismatchEvent event for SessionId {SessionId}.",
|
|
session?.Id);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an audit close session event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="auditEntryId">The audit entry id.</param>
|
|
/// <param name="session">The session object that was created.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
/// <param name="sourceName">Session/CloseSession when the session is closed by request
|
|
/// “Session/Timeout” for a Session timeout
|
|
/// “Session/Terminated” for all other cases.</param>
|
|
public static void ReportAuditCloseSessionEvent(
|
|
this IAuditEventServer? server,
|
|
string auditEntryId,
|
|
ISession session,
|
|
ILogger logger,
|
|
string sourceName = "Session/Terminated")
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ISystemContext systemContext = server.DefaultAuditContext;
|
|
|
|
// raise an audit event.
|
|
var e = new AuditSessionEventState(null);
|
|
|
|
var message = new TranslationInfo(
|
|
"AuditCloseSessionEvent",
|
|
"en-US",
|
|
$"Session with ID:{session?.Id} was closed.");
|
|
|
|
InitializeAuditSessionEvent(systemContext, e, message, true, session!, auditEntryId);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceName, sourceName, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(
|
|
ex,
|
|
"Error while reporting AuditSessionEventState close event for SessionId {SessionId}.",
|
|
session?.Id);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an audit session event for the transfer subscription.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="auditEntryId">The audit entry id.</param>
|
|
/// <param name="session">The session object that was created.</param>
|
|
/// <param name="statusCode">The status code resulting .</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditTransferSubscriptionEvent(
|
|
this IAuditEventServer? server,
|
|
string auditEntryId,
|
|
ISession session,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ISystemContext systemContext = server.DefaultAuditContext;
|
|
|
|
// raise an audit event.
|
|
var e = new AuditSessionEventState(null);
|
|
|
|
var message = new TranslationInfo(
|
|
"AuditSessionEventState",
|
|
"en-US",
|
|
$"Transfer subscription for session ID:{session?.Id} has statusCode {statusCode}.");
|
|
|
|
InitializeAuditSessionEvent(
|
|
systemContext,
|
|
e,
|
|
message,
|
|
StatusCode.IsGood(statusCode),
|
|
session!,
|
|
auditEntryId);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, session!.Id, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"Session/TransferSubscriptions",
|
|
false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(
|
|
ex,
|
|
"Error while reporting AuditSessionEventState close event for SessionId {SessionId}.",
|
|
session?.Id);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raise CertificateUpdatedAudit event
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="objectId">The id of the object used for update certificate method</param>
|
|
/// <param name="method">The method that triggered the audit event.</param>
|
|
/// <param name="inputArguments">The input arguments used to call the method that triggered the audit event.</param>
|
|
/// <param name="certificateGroupId">The id of the certificate group</param>
|
|
/// <param name="certificateTypeId">the certificate type id</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
/// <param name="exception">The exception resulted after executing the UpdateCertificate method. If null, the operation was successfull.</param>
|
|
public static void ReportCertificateUpdatedAuditEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
NodeId objectId,
|
|
MethodState method,
|
|
ArrayOf<Variant> inputArguments,
|
|
NodeId certificateGroupId,
|
|
NodeId certificateTypeId,
|
|
ILogger logger,
|
|
Exception? exception = null)
|
|
{
|
|
try
|
|
{
|
|
var e = new CertificateUpdatedAuditEventState(null);
|
|
|
|
TranslationInfo message = default;
|
|
if (exception == null)
|
|
{
|
|
message = new TranslationInfo(
|
|
"CertificateUpdatedAuditEvent",
|
|
"en-US",
|
|
"CertificateUpdatedAuditEvent.");
|
|
}
|
|
else
|
|
{
|
|
message = new TranslationInfo(
|
|
"CertificateUpdatedAuditEvent",
|
|
"en-US",
|
|
$"CertificateUpdatedAuditEvent - Exception: {exception.Message}.");
|
|
}
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
exception == null,
|
|
DateTime.UtcNow
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, objectId, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"Method/UpdateCertificate",
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.MethodId, method.NodeId, false);
|
|
e.SetChildValue(systemContext, BrowseNames.InputArguments, inputArguments, false);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.CertificateGroup,
|
|
certificateGroupId,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.CertificateType,
|
|
certificateTypeId,
|
|
false);
|
|
|
|
server!.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(
|
|
ex,
|
|
"Error while reporting ReportCertificateUpdatedAuditEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raise CertificateUpdateRequestedAudit event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="objectId">The id of the object used for update certificate method</param>
|
|
/// <param name="method">The method that triggered the audit event.</param>
|
|
/// <param name="inputArguments">The input arguments used to call the method that triggered the audit event.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
/// <param name="exception">
|
|
/// If non-null, indicates the UpdateCertificate operation failed;
|
|
/// the audit event is raised with <c>Status=false</c> and the
|
|
/// exception message is embedded in the localized text.
|
|
/// </param>
|
|
public static void ReportCertificateUpdateRequestedAuditEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
NodeId objectId,
|
|
MethodState method,
|
|
ArrayOf<Variant> inputArguments,
|
|
ILogger logger,
|
|
Exception? exception = null)
|
|
{
|
|
try
|
|
{
|
|
var e = new CertificateUpdateRequestedAuditEventState(null);
|
|
|
|
TranslationInfo message = exception == null
|
|
? new TranslationInfo(
|
|
"CertificateUpdateRequestedAuditEvent",
|
|
"en-US",
|
|
"CertificateUpdateRequestedAuditEvent.")
|
|
: new TranslationInfo(
|
|
"CertificateUpdateRequestedAuditEvent",
|
|
"en-US",
|
|
$"CertificateUpdateRequestedAuditEvent - Exception: {exception.Message}.");
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
exception == null,
|
|
DateTime.UtcNow); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, objectId, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"Method/UpdateCertificate",
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.MethodId, method?.NodeId ?? default, false);
|
|
e.SetChildValue(systemContext, BrowseNames.InputArguments, inputArguments, false);
|
|
|
|
server?.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(
|
|
ex,
|
|
"Error while reporting CertificateUpdateRequestedAuditEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Report the AuditAddNodesEvent
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="addNodesItems">The added nodes information.</param>
|
|
/// <param name="customMessage">Custom message for add nodes audit event.</param>
|
|
/// <param name="statusCode">The resulting status code.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditAddNodesEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
ArrayOf<AddNodesItem> addNodesItems,
|
|
string customMessage,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var e = new AuditAddNodesEventState(null);
|
|
|
|
var message = new TranslationInfo(
|
|
"AuditAddNodesEventState",
|
|
"en-US",
|
|
$"'{customMessage}' returns StatusCode: {statusCode.ToString(null, CultureInfo.InvariantCulture)}.");
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
StatusCode.IsGood(statusCode),
|
|
DateTime.UtcNow
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, ObjectIds.Server, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"NodeManagement/AddNodes",
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.NodesToAdd, addNodesItems, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditAddNodesEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports the AuditDeleteNodesEvent.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="nodesToDelete">The delete nodes information.</param>
|
|
/// <param name="customMessage">Custom message for delete nodes.</param>
|
|
/// <param name="statusCode">The resulting status code.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditDeleteNodesEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
ArrayOf<DeleteNodesItem> nodesToDelete,
|
|
string customMessage,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var e = new AuditDeleteNodesEventState(null);
|
|
|
|
var message = new TranslationInfo(
|
|
"AuditDeleteNodesEventState",
|
|
"en-US",
|
|
$"'{customMessage}' returns StatusCode: {statusCode.ToString(null, CultureInfo.InvariantCulture)}.");
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
StatusCode.IsGood(statusCode),
|
|
DateTime.UtcNow
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, ObjectIds.Server, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"NodeManagement/DeleteNodes",
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.NodesToDelete, nodesToDelete, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditDeleteNodesEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports the AuditAddReferencesEvent.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="referencesToAdd">The references being added.</param>
|
|
/// <param name="customMessage">Custom message for the audit event.</param>
|
|
/// <param name="statusCode">The resulting status code.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditAddReferencesEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
ArrayOf<AddReferencesItem> referencesToAdd,
|
|
string customMessage,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var e = new AuditAddReferencesEventState(null);
|
|
|
|
var message = new TranslationInfo(
|
|
"AuditAddReferencesEventState",
|
|
"en-US",
|
|
$"'{customMessage}' returns StatusCode: {statusCode.ToString(null, CultureInfo.InvariantCulture)}.");
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
StatusCode.IsGood(statusCode),
|
|
DateTime.UtcNow
|
|
);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, ObjectIds.Server, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"NodeManagement/AddReferences",
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.ReferencesToAdd, referencesToAdd, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditAddReferencesEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports the AuditDeleteReferencesEvent.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="referencesToDelete">The references being deleted.</param>
|
|
/// <param name="customMessage">Custom message for the audit event.</param>
|
|
/// <param name="statusCode">The resulting status code.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditDeleteReferencesEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
ArrayOf<DeleteReferencesItem> referencesToDelete,
|
|
string customMessage,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var e = new AuditDeleteReferencesEventState(null);
|
|
|
|
var message = new TranslationInfo(
|
|
"AuditDeleteReferencesEventState",
|
|
"en-US",
|
|
$"'{customMessage}' returns StatusCode: {statusCode.ToString(null, CultureInfo.InvariantCulture)}.");
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
StatusCode.IsGood(statusCode),
|
|
DateTime.UtcNow
|
|
);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, ObjectIds.Server, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"NodeManagement/DeleteReferences",
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.ReferencesToDelete, referencesToDelete, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditDeleteReferencesEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Report the open secure channel audit event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="globalChannelId">The global unique channel id.</param>
|
|
/// <param name="endpointDescription">The endpoint description used for the request.</param>
|
|
/// <param name="request">The incoming <see cref="OpenSecureChannelRequest"/></param>
|
|
/// <param name="clientCertificate">The client certificate.</param>
|
|
/// <param name="exception">The exception resulted from the open secure channel request.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditOpenSecureChannelEvent(
|
|
this IAuditEventServer? server,
|
|
string globalChannelId,
|
|
EndpointDescription endpointDescription,
|
|
OpenSecureChannelRequest request,
|
|
Certificate clientCertificate,
|
|
Exception? exception,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
// raise an audit event.
|
|
var e = new AuditOpenSecureChannelEventState(null);
|
|
TranslationInfo message = default;
|
|
if (exception == null)
|
|
{
|
|
message = new TranslationInfo(
|
|
"AuditOpenSecureChannelEvent",
|
|
"en-US",
|
|
"AuditOpenSecureChannelEvent");
|
|
}
|
|
else
|
|
{
|
|
message = new TranslationInfo(
|
|
"AuditOpenSecureChannelEvent",
|
|
"en-US",
|
|
$"AuditOpenSecureChannelEvent - Exception: {exception.Message}.");
|
|
}
|
|
|
|
StatusCode statusCode = StatusCodes.Good;
|
|
while (exception is not null and not ServiceResultException)
|
|
{
|
|
exception = exception.InnerException;
|
|
}
|
|
|
|
if (exception is ServiceResultException sre)
|
|
{
|
|
if (sre.InnerResult != null)
|
|
{
|
|
statusCode = sre.InnerResult.StatusCode;
|
|
}
|
|
else
|
|
{
|
|
statusCode = sre.StatusCode;
|
|
}
|
|
}
|
|
|
|
ISystemContext systemContext = server.DefaultAuditContext;
|
|
|
|
DateTime actionTimestamp = DateTime.UtcNow;
|
|
if (request?.RequestHeader?.Timestamp != null)
|
|
{
|
|
actionTimestamp = (DateTime)request.RequestHeader.Timestamp;
|
|
}
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
exception == null,
|
|
actionTimestamp
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientUserId
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientAuditEntryId,
|
|
request?.RequestHeader?.AuditEntryId!,
|
|
false);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"SecureChannel/OpenSecureChannel",
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientUserId,
|
|
"System/OpenSecureChannel",
|
|
false);
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, ObjectIds.Server, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
// set AuditSecurityEventType fields
|
|
e.SetChildValue(systemContext, BrowseNames.StatusCodeId, statusCode, false);
|
|
|
|
// set AuditChannelEventType fields
|
|
e.SetChildValue(systemContext, BrowseNames.SecureChannelId, globalChannelId, false);
|
|
|
|
if (clientCertificate != null)
|
|
{
|
|
// set AuditOpenSecureChannelEventType fields
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientCertificate,
|
|
clientCertificate.RawData.ToByteString(),
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientCertificateThumbprint,
|
|
clientCertificate.Thumbprint,
|
|
false);
|
|
}
|
|
if (endpointDescription != null)
|
|
{
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SecurityPolicyUri,
|
|
endpointDescription.SecurityPolicyUri!,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SecurityMode,
|
|
endpointDescription.SecurityMode);
|
|
}
|
|
if (request != null)
|
|
{
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.RequestType,
|
|
request.RequestType);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.RequestedLifetime,
|
|
request.RequestedLifetime,
|
|
false);
|
|
}
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditOpenSecureChannelEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Report the close secure channel audit event.
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="globalChannelId">The global unique channel id.</param>
|
|
/// <param name="exception">The exception resulted from the open secure channel request.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditCloseSecureChannelEvent(
|
|
this IAuditEventServer? server,
|
|
string globalChannelId,
|
|
Exception? exception,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
// raise an audit event.
|
|
var e = new AuditChannelEventState(null);
|
|
|
|
TranslationInfo message = default;
|
|
if (exception == null)
|
|
{
|
|
message = new TranslationInfo(
|
|
"AuditCloseSecureChannelEvent",
|
|
"en-US",
|
|
"AuditCloseSecureChannelEvent");
|
|
}
|
|
else
|
|
{
|
|
message = new TranslationInfo(
|
|
"AuditCloseSecureChannelEvent",
|
|
"en-US",
|
|
$"AuditCloseSecureChannelEvent - Exception: {exception.Message}.");
|
|
}
|
|
|
|
StatusCode statusCode = StatusCodes.Good;
|
|
while (exception is not null and not ServiceResultException)
|
|
{
|
|
exception = exception.InnerException;
|
|
}
|
|
if (exception is ServiceResultException sre)
|
|
{
|
|
statusCode = sre.InnerResult?.StatusCode ?? StatusCodes.Uncertain;
|
|
}
|
|
|
|
ISystemContext systemContext = server.DefaultAuditContext;
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
exception == null,
|
|
DateTime.UtcNow
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceName,
|
|
"SecureChannel/CloseSecureChannel",
|
|
false);
|
|
|
|
const string clientUserId = "System/CloseSecureChannel";
|
|
//operationContext.UserIdentity?.DisplayName, or ”System/CloseSecureChannel”
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.ClientUserId, clientUserId, false);
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, ObjectIds.Server, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
// set AuditSecurityEventType fields
|
|
e.SetChildValue(systemContext, BrowseNames.StatusCodeId, statusCode, false);
|
|
|
|
// set AuditChannelEventType fields
|
|
e.SetChildValue(systemContext, BrowseNames.SecureChannelId, globalChannelId, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditOpenSecureChannelEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports the AuditUpdateMethodEventType
|
|
/// </summary>
|
|
/// <param name="server">The server which reports audit events.</param>
|
|
/// <param name="systemContext">Server information.</param>
|
|
/// <param name="objectId">The id of the object where the method is executed.</param>
|
|
/// <param name="methodId">The NodeId of the object that the method resides in.</param>
|
|
/// <param name="inputArgs">The InputArguments of the method </param>
|
|
/// <param name="customMessage">Custom message for delete nodes.</param>
|
|
/// <param name="statusCode">The resulting status code.</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportAuditUpdateMethodEvent(
|
|
this IAuditEventServer? server,
|
|
ISystemContext systemContext,
|
|
NodeId objectId,
|
|
NodeId methodId,
|
|
ArrayOf<Variant> inputArgs,
|
|
string customMessage,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
if (server?.Auditing != true)
|
|
{
|
|
// current server does not support auditing
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
var e = new AuditUpdateMethodEventState(null);
|
|
|
|
var message = new TranslationInfo(
|
|
"AuditUpdateMethodEventState",
|
|
"en-US",
|
|
$"'{customMessage}' returns StatusCode: {statusCode.ToString(null, CultureInfo.InvariantCulture)}.");
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
StatusCode.IsGood(statusCode),
|
|
DateTime.UtcNow
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, objectId, false);
|
|
e.SetChildValue(systemContext, BrowseNames.SourceName, "Attribute/Call", false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientUserId,
|
|
(systemContext as ISessionSystemContext)?.UserIdentity?.DisplayName!,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientAuditEntryId,
|
|
systemContext.AuditEntryId!,
|
|
false);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.MethodId, methodId, false);
|
|
e.SetChildValue(systemContext, BrowseNames.InputArguments, inputArgs, false);
|
|
|
|
server.ReportAuditEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting AuditDeleteNodesEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an TrustListUpdatedAudit event.
|
|
/// </summary>
|
|
/// <param name="node">The trustlist node.</param>
|
|
/// <param name="systemContext">The current system context</param>
|
|
/// <param name="objectId">The object id where the truest list update methods was called</param>
|
|
/// <param name="sourceName">The source name string</param>
|
|
/// <param name="methodId">The id of the method that was called</param>
|
|
/// <param name="inputParameters">The input parameters of the called method</param>
|
|
/// <param name="statusCode">The status code resulted when the TrustList was updated </param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportTrustListUpdatedAuditEvent(
|
|
this TrustListState node,
|
|
ISystemContext systemContext,
|
|
NodeId objectId,
|
|
string sourceName,
|
|
NodeId methodId,
|
|
ArrayOf<Variant> inputParameters,
|
|
StatusCode statusCode,
|
|
ILogger logger)
|
|
{
|
|
try
|
|
{
|
|
var e = new TrustListUpdatedAuditEventState(null);
|
|
|
|
var message = new TranslationInfo(
|
|
"TrustListUpdatedAuditEvent",
|
|
"en-US",
|
|
$"TrustListUpdatedAuditEvent result is: {statusCode.ToString(null, CultureInfo.InvariantCulture)}");
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
StatusCode.IsGood(statusCode),
|
|
DateTime.UtcNow
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, objectId, false);
|
|
e.SetChildValue(systemContext, BrowseNames.SourceName, sourceName, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.MethodId, methodId, false);
|
|
e.SetChildValue(systemContext, BrowseNames.InputArguments, inputParameters, false);
|
|
|
|
node?.ReportEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error while reporting ReportTrustListUpdatedAuditEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reports an TrustListUpdatedAudit event.
|
|
/// </summary>
|
|
/// <param name="node">The trustlist node.</param>
|
|
/// <param name="systemContext">The current system context</param>
|
|
/// <param name="objectId">The object id where the truest list update methods was called</param>
|
|
/// <param name="sourceName">The source name string</param>
|
|
/// <param name="methodId">The id of the method that was called</param>
|
|
/// <param name="inputParameters">The input parameters of the called method</param>
|
|
/// <param name="logger">A contextual logger to log to</param>
|
|
public static void ReportTrustListUpdateRequestedAuditEvent(
|
|
this TrustListState node,
|
|
ISystemContext systemContext,
|
|
NodeId objectId,
|
|
string sourceName,
|
|
NodeId methodId,
|
|
ArrayOf<Variant> inputParameters,
|
|
ILogger logger)
|
|
{
|
|
try
|
|
{
|
|
var e = new TrustListUpdateRequestedAuditEventState(null);
|
|
|
|
var message = new TranslationInfo(
|
|
"TrustListUpdateRequestedAuditEvent",
|
|
"en-US",
|
|
"TrustListUpdateRequestedAuditEvent.");
|
|
|
|
e.Initialize(systemContext, null, EventSeverity.Min, new LocalizedText(message), true, DateTime.UtcNow); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, objectId, false);
|
|
e.SetChildValue(systemContext, BrowseNames.SourceName, sourceName, false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.LocalTime,
|
|
TimeZoneDataType.Local,
|
|
false);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.MethodId, methodId, false);
|
|
e.SetChildValue(systemContext, BrowseNames.InputArguments, inputParameters, false);
|
|
|
|
node?.ReportEvent(systemContext, e);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(
|
|
ex,
|
|
"Error while reporting TrustListUpdateRequestedAuditEvent event.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize the properties of an AuditUpdateEventState.
|
|
/// </summary>
|
|
/// <param name="e">AuditUpdate event reference</param>
|
|
/// <param name="systemContext">The current system context.</param>
|
|
/// <param name="auditEventName">Audit event name</param>
|
|
/// <param name="sourceName">Source name</param>
|
|
/// <param name="historyUpdateDetails">History update details</param>
|
|
/// <param name="statusCode">The resulting status code</param>
|
|
private static void InitializeAuditHistoryUpdateEvent(
|
|
AuditUpdateEventState e,
|
|
ISystemContext systemContext,
|
|
string auditEventName,
|
|
string sourceName,
|
|
HistoryUpdateDetails historyUpdateDetails,
|
|
StatusCode statusCode)
|
|
{
|
|
var message = new TranslationInfo(
|
|
auditEventName,
|
|
"en-US",
|
|
$"{auditEventName} has Result: {statusCode.ToString(null, CultureInfo.InvariantCulture)}.");
|
|
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
StatusCode.IsGood(statusCode),
|
|
DateTime.UtcNow
|
|
); // initializes Status, ActionTimeStamp, ServerId, ClientAuditEntryId, ClientUserId
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SourceNode,
|
|
historyUpdateDetails.NodeId,
|
|
false);
|
|
e.SetChildValue(systemContext, BrowseNames.SourceName, sourceName, false);
|
|
e.SetChildValue(systemContext, BrowseNames.LocalTime, TimeZoneDataType.Local, false);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientUserId,
|
|
(systemContext as ISessionSystemContext)?.UserIdentity?.DisplayName!,
|
|
false);
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientAuditEntryId,
|
|
systemContext.AuditEntryId!,
|
|
false);
|
|
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ParameterDataTypeId,
|
|
historyUpdateDetails.TypeId,
|
|
false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a session audit event.
|
|
/// </summary>
|
|
private static void InitializeAuditSessionEvent(
|
|
ISystemContext systemContext,
|
|
AuditEventState e,
|
|
TranslationInfo message,
|
|
bool status,
|
|
ISession session,
|
|
string auditEntryId)
|
|
{
|
|
e.Initialize(
|
|
systemContext,
|
|
null,
|
|
EventSeverity.Min,
|
|
new LocalizedText(message),
|
|
status,
|
|
DateTime.UtcNow);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.SourceNode, ObjectIds.Server, false);
|
|
|
|
e.SetChildValue(systemContext, BrowseNames.ClientAuditEntryId, auditEntryId, false);
|
|
if (session != null)
|
|
{
|
|
// set AuditEventType properties
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.ClientUserId,
|
|
session.Identity?.DisplayName!,
|
|
false);
|
|
// set AuditCreateSessionEventType & AuditActivateSessionsEventType properties
|
|
e.SetChildValue(
|
|
systemContext,
|
|
BrowseNames.SecureChannelId,
|
|
session.SecureChannelId,
|
|
false);
|
|
// set AuditSessionEventType
|
|
e.SetChildValue(systemContext, BrowseNames.SessionId, session.Id, false);
|
|
}
|
|
}
|
|
}
|
|
}
|