Initial project state: .NET reference, design, Rust port (M0+M1), evidence
rust / build / test / clippy / fmt (push) Has been cancelled
rust / build / test / clippy / fmt (push) Has been cancelled
Layout:
- src/ .NET 10 x64 reference: MxNativeCodec, MxNativeClient,
MxAsbClient, probes, tests, harnesses. Executable spec.
- design/ Architectural plan for the Rust port (M0–M6), error
model, protocol invariants, risks (R1–R16), adversarial
review log (review.md).
- rust/ Rust workspace. M0 skeleton + M1 codec parity.
mxaccess-codec: 215 unit tests + 2 cross-implementation
parity tests (byte-identical against .NET reference).
Other crates are M0 stubs awaiting M2+.
- captures/ Frida + netsh + pcap evidence per CLAUDE.md
("captures are evidence, not throwaway logs").
- analysis/ Decompiled C# (frida/proxy/decompiled-*),
Ghidra exports for native DLLs (`exports/` only —
working state at `projects/` and AVEVA's input
binaries at `input/` are gitignored).
- docs/ Reverse-engineering reference docs.
- tools/ Setup-LiveProbeEnv.ps1 (Infisical credential fetcher),
Compute-Crc.ps1 (.NET parity helper).
- .github/workflows/ Rust CI: fmt + build + test + clippy on Windows.
- LICENSE MIT (Joseph Doherty, 2026).
Verified:
- cargo test --workspace → 217 passed (215 unit + 2 .NET parity), 0 failed
- cargo clippy --workspace -- -D warnings → clean
- cargo fmt --all -- --check → clean
- cargo publish --dry-run -p mxaccess-codec → packages cleanly
Excluded from history (see .gitignore):
- **/bin, **/obj, **/target — build artifacts
- analysis/ghidra/projects/ — Ghidra working state (regenerable)
- analysis/ghidra/input/ — AVEVA proprietary DLLs (vendor IP)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public enum ArchestrAError
|
||||
{
|
||||
Success = 0,
|
||||
InvalidConnectionId = 1,
|
||||
ApplicationAuthenticationError = 2,
|
||||
UserAuthenticationError = 3,
|
||||
UserAuthorizationError = 4,
|
||||
NotSupportedOperation = 5,
|
||||
MonitoredItemsNotFound = 6,
|
||||
InvalidSubscriptionId = 7,
|
||||
ItemAlreadyRegistered = 8,
|
||||
ItemAlreadyDeletedOrDoesNotExist = 9,
|
||||
InvalidMonitoredItems = 10,
|
||||
OperationFailed = 11,
|
||||
SpecificError = 12,
|
||||
BadNoCommunication = 13,
|
||||
BadNothingToDo = 14,
|
||||
BadTooManyOperations = 15,
|
||||
BadNodeIdInvalid = 16,
|
||||
BrowseFailed = 17,
|
||||
WriteFailedBadOutOfRange = 18,
|
||||
WriteFailedBadTypeMismatch = 19,
|
||||
WriteFailedBadDimensionMismatch = 20,
|
||||
WriteFailedAccessDenied = 21,
|
||||
WriteFailedSecuredWrite = 22,
|
||||
WriteFailedVerifiedWrite = 23,
|
||||
IndexOutOfRange = 24,
|
||||
RequestTimedOut = 25,
|
||||
DataTypeConversionNotSupported = 26,
|
||||
ItemCannotBeRegisteredNoName = 27,
|
||||
ItemCannotBeRegisteredNoId = 28,
|
||||
ItemAlreadyBeingMonitored = 29,
|
||||
SubscriptionIdAlreadyExist = 30,
|
||||
OperationWouldBlock = 31,
|
||||
PublishComplete = 32,
|
||||
WriteFailedUserNotHavingAccessRights = 33,
|
||||
WriteFailedVerifierNotHavingVerifyRights = 34,
|
||||
NotLicensedError = 35,
|
||||
DownstreamDeviceUnavailable = 36,
|
||||
Unknown = 65535
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[Serializable]
|
||||
[XmlType(Namespace = "urn:data.asb.se:2")]
|
||||
public class ArchestrAResult
|
||||
{
|
||||
[XmlElement(ElementName = "Success", Order = 0)]
|
||||
public bool Success { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "ResultCode", Order = 1)]
|
||||
public int ResultCode { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "SpecificErrorCode", Order = 2)]
|
||||
public uint SpecificErrorCode { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "Status", Order = 3)]
|
||||
public uint Status { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "Location", Order = 4)]
|
||||
public string Location { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "SuccessMessages", Order = 5)]
|
||||
public string[] SuccessMessages { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "InformationMessages", Order = 6)]
|
||||
public string[] InformationMessages { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "ErrorMessages", Order = 7)]
|
||||
public string[] ErrorMessages { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "Extensions", Order = 8)]
|
||||
public NamedValue[] Extensions { get; set; }
|
||||
|
||||
[XmlIgnore]
|
||||
public static bool CollectStackTraceInExceptions { get; set; }
|
||||
|
||||
[XmlIgnore]
|
||||
public ArchestrAError ResultCodeAsError
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ResultCode < 65535)
|
||||
{
|
||||
return (ArchestrAError)ResultCode;
|
||||
}
|
||||
return ArchestrAError.Unknown;
|
||||
}
|
||||
set
|
||||
{
|
||||
ResultCode = (int)value;
|
||||
}
|
||||
}
|
||||
|
||||
public static ArchestrAResult Create(ArchestrAResult archestraResult)
|
||||
{
|
||||
ArchestrAResult archestrAResult = archestraResult;
|
||||
if (archestrAResult == null)
|
||||
{
|
||||
archestrAResult = new ArchestrAResult
|
||||
{
|
||||
Success = true,
|
||||
ErrorMessages = new string[0],
|
||||
InformationMessages = new string[0],
|
||||
SuccessMessages = new string[0],
|
||||
Extensions = new NamedValue[0],
|
||||
ResultCode = 0
|
||||
};
|
||||
}
|
||||
return archestrAResult;
|
||||
}
|
||||
|
||||
public static ArchestrAResult MakeGoodResult()
|
||||
{
|
||||
return new ArchestrAResult
|
||||
{
|
||||
ResultCodeAsError = ArchestrAError.Success,
|
||||
Status = 0u,
|
||||
SpecificErrorCode = 0u,
|
||||
Success = true
|
||||
};
|
||||
}
|
||||
|
||||
public static ArchestrAResult MakeResult(ArchestrAError error, ushort status)
|
||||
{
|
||||
return new ArchestrAResult
|
||||
{
|
||||
ResultCodeAsError = error,
|
||||
Status = status,
|
||||
Success = (error == ArchestrAError.Success)
|
||||
};
|
||||
}
|
||||
|
||||
public static ArchestrAResult MakeResult(ArchestrAError error, ushort status, uint specificError)
|
||||
{
|
||||
return new ArchestrAResult
|
||||
{
|
||||
ResultCodeAsError = error,
|
||||
Status = status,
|
||||
SpecificErrorCode = specificError,
|
||||
Success = (error == ArchestrAError.Success)
|
||||
};
|
||||
}
|
||||
|
||||
public static ArchestrAResult MakeErrorResult(string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
throw new ArgumentNullException("message");
|
||||
}
|
||||
return MakeResult(ArchestrAError.Unknown, 0, 0u).AddErrorMessage(message);
|
||||
}
|
||||
|
||||
public static ArchestrAResult MakeErrorResult(ArchestrAError error, string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
throw new ArgumentNullException("message");
|
||||
}
|
||||
return MakeResult(ArchestrAError.Unknown, 0, 0u).AddErrorMessage(message);
|
||||
}
|
||||
|
||||
public static ArchestrAResult MakeNotLicensedResult(string message)
|
||||
{
|
||||
string message2 = (string.IsNullOrWhiteSpace(message) ? "The requested connection is not licensed for the operation and will be terminated" : message);
|
||||
return MakeResult(ArchestrAError.NotLicensedError, 0, 0u).AddErrorMessage(message2);
|
||||
}
|
||||
}
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public static class ArchestrAResultExtensions
|
||||
{
|
||||
public static ArchestrAResult AddSuccessMessage(this ArchestrAResult result, [Localizable(false)] string message, params object[] arguments)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
result.AddSuccessMessage(FormatMessage(message, arguments));
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ArchestrAResult AddSuccessMessage(this ArchestrAResult result, [Localizable(false)] string message)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (result.SuccessMessages == null)
|
||||
{
|
||||
result.SuccessMessages = new string[0];
|
||||
}
|
||||
List<string> list = result.SuccessMessages.ToList();
|
||||
list.Add(message);
|
||||
result.SuccessMessages = list.ToArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ArchestrAResult AddInformationMessage(this ArchestrAResult result, [Localizable(false)] string message, params object[] arguments)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
result.AddInformationMessage(FormatMessage(message, arguments));
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ArchestrAResult AddInformationMessage(this ArchestrAResult result, [Localizable(false)] string message)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (result.InformationMessages == null)
|
||||
{
|
||||
result.InformationMessages = new string[0];
|
||||
}
|
||||
List<string> list = result.InformationMessages.ToList();
|
||||
list.Add(message);
|
||||
result.InformationMessages = list.ToArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ArchestrAResult AddErrorMessage(this ArchestrAResult result, [Localizable(false)] string message, params object[] arguments)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
result.AddErrorMessage(FormatMessage(message, arguments));
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ArchestrAResult AddErrorMessage(this ArchestrAResult result, [Localizable(false)] string message)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (result.ErrorMessages == null)
|
||||
{
|
||||
result.ErrorMessages = new string[0];
|
||||
}
|
||||
List<string> list = result.ErrorMessages.ToList();
|
||||
list.Add(message);
|
||||
result.ErrorMessages = list.ToArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ArchestrAResult AddErrorMessages(this ArchestrAResult result, IEnumerable<string> messages)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (messages == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (result.ErrorMessages == null)
|
||||
{
|
||||
result.ErrorMessages = new string[0];
|
||||
}
|
||||
List<string> list = result.ErrorMessages.ToList();
|
||||
list.AddRange(messages);
|
||||
result.ErrorMessages = list.ToArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ArchestrAResult AddExtension(this ArchestrAResult result, NamedValue extension)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (extension == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (result.Extensions == null)
|
||||
{
|
||||
result.Extensions = new NamedValue[0];
|
||||
}
|
||||
List<NamedValue> list = result.Extensions.ToList();
|
||||
list.Add(extension);
|
||||
result.Extensions = list.ToArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ArchestrAResult AddExtension(this ArchestrAResult result, [Localizable(false)] string name, object value)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (result.Extensions == null)
|
||||
{
|
||||
result.Extensions = new NamedValue[0];
|
||||
}
|
||||
List<NamedValue> list = result.Extensions.ToList();
|
||||
list.Add(new NamedValue
|
||||
{
|
||||
Name = name,
|
||||
Value = Value.Create(value)
|
||||
});
|
||||
result.Extensions = list.ToArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ArchestrAResult Concatenate(this ArchestrAResult thisResult, ArchestrAResult result)
|
||||
{
|
||||
if (thisResult == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (result != null)
|
||||
{
|
||||
if (!result.Success)
|
||||
{
|
||||
thisResult.Success = false;
|
||||
}
|
||||
if (result.ErrorMessages != null)
|
||||
{
|
||||
if (thisResult.ErrorMessages == null)
|
||||
{
|
||||
thisResult.ErrorMessages = new string[0];
|
||||
}
|
||||
List<string> list = thisResult.ErrorMessages.ToList();
|
||||
list.AddRange(result.ErrorMessages);
|
||||
thisResult.ErrorMessages = list.ToArray();
|
||||
}
|
||||
if (result.InformationMessages != null)
|
||||
{
|
||||
if (thisResult.InformationMessages == null)
|
||||
{
|
||||
thisResult.InformationMessages = new string[0];
|
||||
}
|
||||
List<string> list2 = thisResult.InformationMessages.ToList();
|
||||
list2.AddRange(result.InformationMessages);
|
||||
thisResult.ErrorMessages = list2.ToArray();
|
||||
}
|
||||
if (result.SuccessMessages != null)
|
||||
{
|
||||
if (thisResult.SuccessMessages == null)
|
||||
{
|
||||
thisResult.SuccessMessages = new string[0];
|
||||
}
|
||||
List<string> list3 = thisResult.SuccessMessages.ToList();
|
||||
list3.AddRange(result.SuccessMessages);
|
||||
thisResult.SuccessMessages = list3.ToArray();
|
||||
}
|
||||
if (result.Extensions != null)
|
||||
{
|
||||
if (thisResult.Extensions == null)
|
||||
{
|
||||
thisResult.Extensions = new NamedValue[0];
|
||||
}
|
||||
List<NamedValue> list4 = thisResult.Extensions.ToList();
|
||||
list4.AddRange(result.Extensions);
|
||||
thisResult.Extensions = list4.ToArray();
|
||||
}
|
||||
if (thisResult.ResultCode == 0)
|
||||
{
|
||||
thisResult.ResultCode = result.ResultCode;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string ToFormattedString(this ArchestrAResult result)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (result.Success)
|
||||
{
|
||||
stringBuilder.Append("Success");
|
||||
if (result.SuccessMessages != null && result.SuccessMessages.Length != 0)
|
||||
{
|
||||
stringBuilder.Append(": ");
|
||||
stringBuilder.Append(string.Join("| ", result.SuccessMessages));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.Append("Failure");
|
||||
if (result.ErrorMessages != null && result.ErrorMessages.Length != 0)
|
||||
{
|
||||
stringBuilder.Append(": ");
|
||||
stringBuilder.Append(string.Join("| ", result.ErrorMessages));
|
||||
}
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
private static string FormatMessage([Localizable(false)] string message, params object[] arguments)
|
||||
{
|
||||
string result = message;
|
||||
if (arguments != null && arguments.Length != 0)
|
||||
{
|
||||
result = string.Format(CultureInfo.CurrentCulture, message, arguments);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "AuthenticateMe", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
[XmlRoot(Namespace = "urn:msg.asb.se:2")]
|
||||
public class AuthenticateMeRequest : ConnectedRequest
|
||||
{
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 0)]
|
||||
[XmlElement(ElementName = "ConsumerAuthenticationData")]
|
||||
public AuthenticationData ConsumerAuthenticationData { get; set; }
|
||||
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 1)]
|
||||
[XmlElement(ElementName = "ConsumerMetaData")]
|
||||
public ClientMetadata ConsumerMetaData { get; set; }
|
||||
|
||||
public AuthenticateMeRequest()
|
||||
{
|
||||
}
|
||||
|
||||
public AuthenticateMeRequest(AuthenticationData consumerAuthenticationData, ClientMetadata consumerMetaData)
|
||||
{
|
||||
ConsumerAuthenticationData = consumerAuthenticationData;
|
||||
ConsumerMetaData = consumerMetaData;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "AuthenticateMeResponse", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
[XmlRoot(Namespace = "urn:msg.asb.se:2")]
|
||||
public class AuthenticateMeResponse : ConnectedResponse
|
||||
{
|
||||
public AuthenticateMeResponse()
|
||||
{
|
||||
}
|
||||
|
||||
public AuthenticateMeResponse(ArchestrAResult result, ConnectionState downstreamConnectionState)
|
||||
: base(result, downstreamConnectionState)
|
||||
{
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[Serializable]
|
||||
[XmlType(Namespace = "urn:data.asb.se:2")]
|
||||
public class AuthenticationData
|
||||
{
|
||||
[XmlElement(ElementName = "Data", DataType = "base64Binary", Order = 0)]
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "InitializationVector", DataType = "base64Binary", Order = 1)]
|
||||
public byte[] InitializationVector { get; set; }
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public static class AuthenticationDataExtensions
|
||||
{
|
||||
public static bool AreEqual(this AuthenticationData value, AuthenticationData other)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (value != null && value.Data != null && other != null && other.Data != null)
|
||||
{
|
||||
return value.AreEqual(other.Data);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool AreEqual(this AuthenticationData value, byte[] expected)
|
||||
{
|
||||
bool result = false;
|
||||
try
|
||||
{
|
||||
if (value != null && value.Data != null && expected != null && value.Data.Length == expected.Length)
|
||||
{
|
||||
result = true;
|
||||
for (int i = 0; i < value.Data.Length; i++)
|
||||
{
|
||||
if (value.Data[i] != expected[i])
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,661 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Discovery;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using ArchestrAServices.Common;
|
||||
using ArchestrAServices.Proxy;
|
||||
using Asb.Base.V2.ContractTypes;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public class BaseV2Client<T> : IBaseV2, IDisposable where T : class, IAuthenticateAsb
|
||||
{
|
||||
private readonly object connectionLock = new object();
|
||||
|
||||
private readonly string connectionUser = string.Empty;
|
||||
|
||||
private readonly string connectionApplication = string.Empty;
|
||||
|
||||
private readonly TimeSpan timeToWaitForConnection = DefaultTimeToWaitForConnection;
|
||||
|
||||
private IClientManagement clientManager;
|
||||
|
||||
private long connectionInProgress;
|
||||
|
||||
private ConnectContext<T> successfulConnectionResult;
|
||||
|
||||
private ManualResetEvent connectionEstablishedEvent;
|
||||
|
||||
private bool disposed;
|
||||
|
||||
public static TimeSpan DefaultTimeToWaitForConnection => TimeSpan.FromSeconds(20.0);
|
||||
|
||||
public SecureCommunicationModes SecureCommunicationMode { get; set; }
|
||||
|
||||
public DateTime ServiceLoadTime { get; set; }
|
||||
|
||||
public bool Connected
|
||||
{
|
||||
get
|
||||
{
|
||||
if (successfulConnectionResult?.ServiceChannel != null)
|
||||
{
|
||||
return successfulConnectionResult.ServiceChannel.State == CommunicationState.Opened;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public CommunicationState State
|
||||
{
|
||||
get
|
||||
{
|
||||
if (successfulConnectionResult != null && successfulConnectionResult.ServiceChannel != null)
|
||||
{
|
||||
return successfulConnectionResult.ServiceChannel.State;
|
||||
}
|
||||
return CommunicationState.Closed;
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectionState DownstreamConnectionState { get; protected set; }
|
||||
|
||||
internal ConnectContext<T> Context => successfulConnectionResult;
|
||||
|
||||
protected T ChannelClient => successfulConnectionResult.ServiceClient;
|
||||
|
||||
public Guid ConnectionId
|
||||
{
|
||||
get
|
||||
{
|
||||
if (successfulConnectionResult != null)
|
||||
{
|
||||
return successfulConnectionResult.ConnectionId;
|
||||
}
|
||||
return Guid.Empty;
|
||||
}
|
||||
set
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public BaseV2Client()
|
||||
{
|
||||
clientManager = new ClientManagement();
|
||||
SecureCommunicationMode = RegistryHandler.SecureCommunicationMode;
|
||||
}
|
||||
|
||||
public BaseV2Client(TimeSpan connectTimeout)
|
||||
: this()
|
||||
{
|
||||
if (connectTimeout.TotalMilliseconds > 2147483647.0 || connectTimeout < TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("connectTimeout");
|
||||
}
|
||||
timeToWaitForConnection = connectTimeout;
|
||||
}
|
||||
|
||||
internal BaseV2Client(TimeSpan connectTimeout, IClientManagement manager)
|
||||
: this(manager)
|
||||
{
|
||||
if (connectTimeout.TotalMilliseconds > 2147483647.0 || connectTimeout < TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("connectTimeout");
|
||||
}
|
||||
timeToWaitForConnection = connectTimeout;
|
||||
}
|
||||
|
||||
internal BaseV2Client(TimeSpan connectTimeout, IClientManagement manager, string application, string user)
|
||||
: this(connectTimeout, manager)
|
||||
{
|
||||
connectionApplication = application;
|
||||
connectionUser = user;
|
||||
}
|
||||
|
||||
public BaseV2Client(TimeSpan connectTimeout, string application, string user)
|
||||
: this(connectTimeout)
|
||||
{
|
||||
connectionApplication = application;
|
||||
connectionUser = user;
|
||||
}
|
||||
|
||||
internal BaseV2Client(IClientManagement manager)
|
||||
{
|
||||
clientManager = manager;
|
||||
SecureCommunicationMode = RegistryHandler.SecureCommunicationMode;
|
||||
}
|
||||
|
||||
public bool Connect(EndpointDiscoveryMetadata selectedMetadata, string solutionName, ClientAccess access, Action<string> errorCallback)
|
||||
{
|
||||
return Connect(selectedMetadata, solutionName, access, errorCallback, useSecureConnection: false);
|
||||
}
|
||||
|
||||
public bool Connect(EndpointDiscoveryMetadata selectedMetadata, string solutionName, ClientAccess access, Action<string> errorCallback, bool useSecureConnection)
|
||||
{
|
||||
ServiceTrace.LogCsv("Connect (endpoint discovered) entry");
|
||||
ServiceTrace.LogResume("Connect (endpoint discovered) entry");
|
||||
if (Interlocked.CompareExchange(ref connectionInProgress, 1L, 0L) != 0L)
|
||||
{
|
||||
string text = string.Format(CultureInfo.InvariantCulture, "Connect: another connection is in progress, cannot attempt a connection using selected discovery metadata");
|
||||
ServiceTrace.LogError(text);
|
||||
errorCallback?.Invoke(text);
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
bool flag = EstablishConnection(selectedMetadata, solutionName, access, errorCallback, useSecureConnection);
|
||||
ServiceTrace.LogSuspend("Connect (endpoint discovered) exit, {0}", flag ? "succeeded" : "failed");
|
||||
ServiceTrace.LogCsv("Connect (endpoint discovered) exit [success]", flag);
|
||||
return flag;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Exchange(ref connectionInProgress, 0L);
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
ServiceTrace.LogCsv("Disconnect entry");
|
||||
ServiceTrace.LogResume("Disconnect entry");
|
||||
if (successfulConnectionResult == null || !successfulConnectionResult.Success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SystemAuthenticationClientAuthentication.DisconnectSecureSession(successfulConnectionResult.ConnectionId, delegate(DisconnectRequest request)
|
||||
{
|
||||
if (successfulConnectionResult != null && successfulConnectionResult.Success)
|
||||
{
|
||||
if (successfulConnectionResult.ServiceClient != null && successfulConnectionResult.ServiceChannel != null && successfulConnectionResult.ServiceChannel.State != CommunicationState.Faulted)
|
||||
{
|
||||
ServiceTrace.LogCsv("Calling WCF channel Disconnect method");
|
||||
successfulConnectionResult.ServiceClient.Disconnect(request);
|
||||
ServiceTrace.LogCsv("Returned from WCF channel Disconnect method");
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
});
|
||||
DownstreamConnectionState = ConnectionState.Unknown;
|
||||
ServiceTrace.LogSuspend("Disconnect exit");
|
||||
ServiceTrace.LogCsv("Disconnect exit");
|
||||
}
|
||||
|
||||
public void Abort()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
internal void InjectClientManager(IClientManagement manager)
|
||||
{
|
||||
clientManager = manager;
|
||||
}
|
||||
|
||||
protected static ReadOnlyCollection<EndpointDiscoveryMetadata> DiscoverEndpoints(XmlQualifiedName contract, string scopeRule)
|
||||
{
|
||||
return DiscoverEndpoints(contract, scopeRule, null);
|
||||
}
|
||||
|
||||
protected static ReadOnlyCollection<EndpointDiscoveryMetadata> DiscoverEndpoints(XmlQualifiedName contract, string scopeRule, bool? useSecureConnection)
|
||||
{
|
||||
List<EndpointDiscoveryMetadata> list = new ClientManagement().DiscoverEndpointsForContract(contract, scopeRule)?.ToList();
|
||||
if (list == null)
|
||||
{
|
||||
ServiceTrace.LogWarning(string.Format(CultureInfo.InvariantCulture, "DiscoverEndpoints: null FindResponse finding contract {0} with access name {1}", new object[2] { contract.Name, scopeRule }));
|
||||
return new ReadOnlyCollection<EndpointDiscoveryMetadata>(new EndpointDiscoveryMetadata[0]);
|
||||
}
|
||||
if (list.Count == 0)
|
||||
{
|
||||
ServiceTrace.LogInfo(string.Format(CultureInfo.InvariantCulture, "DiscoverEndpoints found no endpoints for contract {0} with access name {1}", new object[2] { contract.Name, scopeRule }));
|
||||
return new ReadOnlyCollection<EndpointDiscoveryMetadata>(new EndpointDiscoveryMetadata[0]);
|
||||
}
|
||||
return new ReadOnlyCollection<EndpointDiscoveryMetadata>(list);
|
||||
}
|
||||
|
||||
protected bool Connect(XmlQualifiedName contract, string scopeRule, ClientAccess access, Action<string> errorCallback)
|
||||
{
|
||||
return Connect(contract, scopeRule, access, errorCallback, useSecureConnection: false);
|
||||
}
|
||||
|
||||
protected bool Connect(XmlQualifiedName contract, string scopeRule, ClientAccess access, Action<string> errorCallback, bool useSecureConnection)
|
||||
{
|
||||
ServiceTrace.LogCsv("Connect (discover endpoint) entry");
|
||||
ServiceTrace.LogResume("Connect (discover endpoint) entry");
|
||||
if (contract == null)
|
||||
{
|
||||
string text = string.Format(CultureInfo.InvariantCulture, "Connect: no contract provided, no connection is possible");
|
||||
ServiceTrace.LogError(text);
|
||||
errorCallback?.Invoke(text);
|
||||
return false;
|
||||
}
|
||||
if (Interlocked.CompareExchange(ref connectionInProgress, 1L, 0L) != 0L)
|
||||
{
|
||||
string text2 = string.Format(CultureInfo.InvariantCulture, "Connect: another connection is in progress, cannot attempt a connection with a specified contract and access name");
|
||||
ServiceTrace.LogError(text2);
|
||||
errorCallback?.Invoke(text2);
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
List<EndpointDiscoveryMetadata> list = clientManager.DiscoverEndpointsForContract(contract, scopeRule)?.ToList();
|
||||
if (list == null)
|
||||
{
|
||||
string text3 = string.Format(CultureInfo.InvariantCulture, "Connect: null FindResponse finding contract {0} with access name {1}", new object[2] { contract.Name, scopeRule });
|
||||
ServiceTrace.LogError(text3);
|
||||
errorCallback?.Invoke(text3);
|
||||
return false;
|
||||
}
|
||||
if (list.Count == 0)
|
||||
{
|
||||
string text4 = string.Format(CultureInfo.InvariantCulture, "Connect found no endpoints for contract {0} with access name {1}", new object[2] { contract.Name, scopeRule });
|
||||
ServiceTrace.LogError(text4);
|
||||
errorCallback?.Invoke(text4);
|
||||
return false;
|
||||
}
|
||||
EndpointDiscoveryMetadata selectedMetadata;
|
||||
if (list.Count > 1)
|
||||
{
|
||||
Random random = new Random(DateTime.Now.Millisecond);
|
||||
selectedMetadata = list[random.Next(list.Count)];
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedMetadata = list[0];
|
||||
}
|
||||
bool flag = EstablishConnection(selectedMetadata, string.Empty, access, errorCallback, useSecureConnection);
|
||||
ServiceTrace.LogSuspend("Connect (discover endpoint) exit, {0}", flag ? "succeeded" : "failed");
|
||||
ServiceTrace.LogCsv("Connect (discover endpoint) exit [success]", flag);
|
||||
return flag;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Exchange(ref connectionInProgress, 0L);
|
||||
}
|
||||
}
|
||||
|
||||
protected UserTokenContract EncryptUserToken(UserToken originalToken)
|
||||
{
|
||||
if (originalToken == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
UserTokenContract userTokenContract = new UserTokenContract
|
||||
{
|
||||
Encryption = originalToken.Encryption,
|
||||
HostName = originalToken.HostName,
|
||||
IdType = originalToken.IdType,
|
||||
LocationId = originalToken.LocationId,
|
||||
UserName = originalToken.UserName,
|
||||
Validity = originalToken.Validity
|
||||
};
|
||||
SystemAuthenticationClientAuthentication clientAuthenticator = SysAuthenticatorClientCache.GetClientAuthenticator(successfulConnectionResult.ConnectionId);
|
||||
if (clientAuthenticator == null || !clientAuthenticator.SecureSessionEstablished)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot encrypt user token due to session in wrong state");
|
||||
}
|
||||
if (originalToken.Password != null)
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(originalToken.Password);
|
||||
userTokenContract.Password = clientAuthenticator.Encypher(bytes, clientAuthenticator.EncryptionKey);
|
||||
}
|
||||
if (originalToken.SamlToken != null)
|
||||
{
|
||||
userTokenContract.SamlToken = clientAuthenticator.Encypher(originalToken.SamlToken, clientAuthenticator.EncryptionKey);
|
||||
}
|
||||
if (originalToken.JwtToken != null)
|
||||
{
|
||||
byte[] bytes2 = Encoding.UTF8.GetBytes(originalToken.JwtToken);
|
||||
userTokenContract.JwtToken = clientAuthenticator.Encypher(bytes2, clientAuthenticator.EncryptionKey);
|
||||
}
|
||||
if (originalToken.X509Certificate != null)
|
||||
{
|
||||
userTokenContract.X509Certificate = clientAuthenticator.Encypher(originalToken.X509Certificate, clientAuthenticator.EncryptionKey);
|
||||
}
|
||||
return userTokenContract;
|
||||
}
|
||||
|
||||
protected void Reset()
|
||||
{
|
||||
successfulConnectionResult?.Dispose();
|
||||
successfulConnectionResult = null;
|
||||
}
|
||||
|
||||
private bool EstablishConnection(EndpointDiscoveryMetadata selectedMetadata, string solutionName, ClientAccess access, Action<string> errorCallback, bool useSecureConnection)
|
||||
{
|
||||
ServiceTrace.LogCsv("CreateConnectContext entry");
|
||||
ServiceTrace.LogResume("CreateConnectContext entry");
|
||||
if (selectedMetadata == null)
|
||||
{
|
||||
errorCallback?.Invoke("No discovery endpoint metadata provided, cannot connect");
|
||||
return false;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(solutionName))
|
||||
{
|
||||
solutionName = new ASBSolutionManager().GetASBSolutionName(selectedMetadata, out var errorMessage);
|
||||
if (!string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
errorCallback?.Invoke(errorMessage);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
lock (connectionLock)
|
||||
{
|
||||
successfulConnectionResult = null;
|
||||
connectionEstablishedEvent = new ManualResetEvent(initialState: false);
|
||||
}
|
||||
using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource())
|
||||
{
|
||||
SpinConnectionTasks(selectedMetadata.ListenUris, solutionName, access, errorCallback, useSecureConnection, cancellationTokenSource.Token);
|
||||
bool flag = connectionEstablishedEvent.WaitOne(timeToWaitForConnection);
|
||||
ServiceTrace.LogVerbose("{0} task connected with the service", flag ? "A" : "No");
|
||||
cancellationTokenSource.Cancel();
|
||||
}
|
||||
bool result;
|
||||
lock (connectionLock)
|
||||
{
|
||||
connectionEstablishedEvent.Dispose();
|
||||
connectionEstablishedEvent = null;
|
||||
result = true;
|
||||
if (successfulConnectionResult == null)
|
||||
{
|
||||
result = false;
|
||||
errorCallback?.Invoke("No connection was established with a service endpoint");
|
||||
}
|
||||
}
|
||||
ServiceTrace.LogSuspend("CreateConnectContext exit");
|
||||
ServiceTrace.LogCsv("CreateConnectContext exit");
|
||||
return result;
|
||||
}
|
||||
|
||||
private void SpinConnectionTasks(Collection<Uri> listenUris, string solutionName, ClientAccess access, Action<string> errorCallback, bool useSecureConnection, CancellationToken cancellationToken)
|
||||
{
|
||||
ServiceTrace.LogVerbose("Spinning tasks for {0} listen Uris", listenUris.Count);
|
||||
foreach (Uri endpointUri in listenUris)
|
||||
{
|
||||
string selectedEndpointUri = endpointUri.ToString();
|
||||
Task.Factory.StartNew(delegate
|
||||
{
|
||||
NetTcpBindingSecurityMode securityMode = (useSecureConnection ? NetTcpBindingSecurityMode.CertificateEncryption : NetTcpBindingSecurityMode.None);
|
||||
Binding binding = SvcUtilities.GetBinding(selectedEndpointUri, securityMode);
|
||||
binding.OpenTimeout = new TimeSpan(0, 10, 0);
|
||||
binding.ReceiveTimeout = new TimeSpan(0, 10, 0);
|
||||
binding.SendTimeout = new TimeSpan(0, 10, 0);
|
||||
binding.CloseTimeout = new TimeSpan(0, 10, 0);
|
||||
EndpointAddress serviceEndpointAddress = new EndpointAddress(selectedEndpointUri);
|
||||
if (useSecureConnection)
|
||||
{
|
||||
IPHostEntry hostEntry = Dns.GetHostEntry(endpointUri.Host);
|
||||
serviceEndpointAddress = new EndpointAddress(endpointUri, EndpointIdentity.CreateDnsIdentity(hostEntry.HostName));
|
||||
}
|
||||
InternalConnect(serviceEndpointAddress, binding, solutionName, access, errorCallback, cancellationToken);
|
||||
}, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
|
||||
}
|
||||
}
|
||||
|
||||
private void InternalConnect(EndpointAddress serviceEndpointAddress, Binding binding, string solutionName, ClientAccess access, Action<string> errorCallback, CancellationToken cancellationToken)
|
||||
{
|
||||
if (serviceEndpointAddress == null)
|
||||
{
|
||||
throw new ArgumentNullException("serviceEndpointAddress");
|
||||
}
|
||||
if (binding == null)
|
||||
{
|
||||
throw new ArgumentNullException("binding");
|
||||
}
|
||||
if (string.IsNullOrEmpty(solutionName))
|
||||
{
|
||||
throw new ArgumentNullException("solutionName");
|
||||
}
|
||||
try
|
||||
{
|
||||
EstablishChannelConnection(serviceEndpointAddress, binding, solutionName, access, errorCallback, cancellationToken);
|
||||
}
|
||||
catch (CommunicationException ex)
|
||||
{
|
||||
ServiceTrace.LogWarning("BaseV2Client caught CommunicationException opening channel: " + ex.Message);
|
||||
errorCallback?.Invoke("BaseV2Client caught CommunicationException opening channel: " + ex.Message);
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
ServiceTrace.LogWarning(" " + ex.InnerException.Message);
|
||||
errorCallback?.Invoke(" " + ex.InnerException.Message);
|
||||
}
|
||||
}
|
||||
catch (TimeoutException ex2)
|
||||
{
|
||||
ServiceTrace.LogWarning("BaseV2Client caught TimeoutException opening channel: " + ex2.Message);
|
||||
errorCallback?.Invoke("BaseV2Client caught TimeoutException opening channel: " + ex2.Message);
|
||||
if (ex2.InnerException != null)
|
||||
{
|
||||
ServiceTrace.LogWarning(" " + ex2.InnerException.Message);
|
||||
errorCallback?.Invoke(" " + ex2.InnerException.Message);
|
||||
}
|
||||
}
|
||||
catch (Exception ex3)
|
||||
{
|
||||
ServiceTrace.LogWarning("BaseV2Client caught exception opening channel: " + ex3.Message);
|
||||
errorCallback?.Invoke("BaseV2Client caught exception opening channel: " + ex3.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void EstablishChannelConnection(EndpointAddress serviceEndpointAddress, Binding binding, string solutionName, ClientAccess access, Action<string> errorCallback, CancellationToken cancellationToken)
|
||||
{
|
||||
ConnectContext<T> connectContext = clientManager.CreateConnectContext<T>(serviceEndpointAddress, binding, connectionApplication, connectionUser, cancellationToken);
|
||||
if ((connectContext != null && connectContext.ServiceChannelFactory?.State == CommunicationState.Closed) || (connectContext != null && connectContext.ServiceChannel?.State == CommunicationState.Closed))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (clientManager.EstablishSecureSession(connectContext, solutionName, access))
|
||||
{
|
||||
ServiceTrace.LogVerbose("Secure connection with endpoint {0} is established using solution {1}", serviceEndpointAddress.Uri.AbsoluteUri, solutionName);
|
||||
bool flag = false;
|
||||
lock (connectionLock)
|
||||
{
|
||||
if (successfulConnectionResult == null)
|
||||
{
|
||||
flag = true;
|
||||
successfulConnectionResult = connectContext;
|
||||
ServiceTrace.LogVerbose("Secure connection with endpoint {0} using solution {1} is first, signal success", serviceEndpointAddress.Uri.AbsoluteUri, solutionName);
|
||||
connectionEstablishedEvent?.Set();
|
||||
}
|
||||
}
|
||||
if (!flag)
|
||||
{
|
||||
ServiceTrace.LogVerbose("Secure connection with endpoint {0} using solution {1} is NOT first, disconnect", serviceEndpointAddress.Uri.AbsoluteUri, solutionName);
|
||||
clientManager.DisconnectSecureSession(connectContext);
|
||||
connectContext.Dispose();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (connectContext != null && !string.IsNullOrEmpty(connectContext.ErrorMessage))
|
||||
{
|
||||
errorCallback?.Invoke(connectContext.ErrorMessage);
|
||||
}
|
||||
ServiceTrace.LogVerbose("Connection with endpoint {0} using solution {1} rejected", serviceEndpointAddress.Uri.AbsoluteUri, solutionName);
|
||||
if (connectionEstablishedEvent != null)
|
||||
{
|
||||
connectionEstablishedEvent.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnConnect(ulong timeout, ConsumerMetadata metadata)
|
||||
{
|
||||
ServiceTrace.LogCsv("OnConnect entry");
|
||||
ServiceTrace.LogResume("OnConnect entry");
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ArchestrAResult KeepAlive()
|
||||
{
|
||||
ServiceTrace.LogCsv("KeepAlive entry");
|
||||
ServiceTrace.LogResume("KeepAlive entry");
|
||||
ArchestrAResult archestrAResult = PrepareToSend(new KeepAliveRequest(), delegate(ConnectedRequest request, T channelClient)
|
||||
{
|
||||
ServiceTrace.LogVerbose("Calling WCF channel KeepAlive method");
|
||||
KeepAliveResponse keepAliveResponse = channelClient.KeepAlive((KeepAliveRequest)request);
|
||||
ServiceTrace.LogVerbose("Returned from WCF channel KeepAlive method");
|
||||
if (keepAliveResponse == null)
|
||||
{
|
||||
return ArchestrAResult.MakeResult(ArchestrAError.BadNoCommunication, 0).AddErrorMessage("No response from the service");
|
||||
}
|
||||
UpdateDownstreamConnectionState(keepAliveResponse);
|
||||
return keepAliveResponse.Result;
|
||||
});
|
||||
ServiceTrace.LogSuspend("KeepAlive exit with {0}", archestrAResult.Success ? "success" : "failure");
|
||||
ServiceTrace.LogCsv("KeepAlive exit [success]", archestrAResult.Success);
|
||||
return archestrAResult;
|
||||
}
|
||||
|
||||
public ArchestrAResult GetStatusItems(out string[] items)
|
||||
{
|
||||
ServiceTrace.LogCsv("GetStatusItems entry");
|
||||
ServiceTrace.LogResume("GetStatusItems entry");
|
||||
string[] receivedItems = null;
|
||||
ArchestrAResult archestrAResult = PrepareToSend(new GetStatusItemsRequest(), delegate(ConnectedRequest request, T channelClient)
|
||||
{
|
||||
ServiceTrace.LogCsv("Calling WCF channel GetStatusItems method");
|
||||
GetStatusItemsResponse statusItems = channelClient.GetStatusItems((GetStatusItemsRequest)request);
|
||||
ServiceTrace.LogCsv("Returned from WCF channel GetStatusItems method");
|
||||
if (statusItems == null)
|
||||
{
|
||||
return ArchestrAResult.MakeResult(ArchestrAError.BadNoCommunication, 0).AddErrorMessage("No response from the service");
|
||||
}
|
||||
UpdateDownstreamConnectionState(statusItems);
|
||||
receivedItems = statusItems.Items;
|
||||
return statusItems.Result;
|
||||
});
|
||||
ServiceTrace.LogSuspend("GetStatusItems exit with {0}", archestrAResult.Success ? "success" : "failure");
|
||||
ServiceTrace.LogCsv("GetStatusItems exit [success]", archestrAResult.Success);
|
||||
items = receivedItems;
|
||||
return archestrAResult;
|
||||
}
|
||||
|
||||
public ArchestrAResult GetStatus(string[] itemsToReturn, out NamedValue[] items)
|
||||
{
|
||||
ServiceTrace.LogCsv("GetStatus entry");
|
||||
ServiceTrace.LogResume("GetStatus entry");
|
||||
NamedValue[] receivedItems = null;
|
||||
ArchestrAResult archestrAResult = PrepareToSend(new GetStatusRequest
|
||||
{
|
||||
ItemsToReturn = itemsToReturn
|
||||
}, delegate(ConnectedRequest request, T channelClient)
|
||||
{
|
||||
ServiceTrace.LogCsv("Calling WCF channel GetStatus method");
|
||||
GetStatusResponse status = channelClient.GetStatus((GetStatusRequest)request);
|
||||
ServiceTrace.LogCsv("Returned from WCF channel GetStatus method");
|
||||
if (status == null)
|
||||
{
|
||||
return ArchestrAResult.MakeResult(ArchestrAError.BadNoCommunication, 0).AddErrorMessage("No response from the service");
|
||||
}
|
||||
UpdateDownstreamConnectionState(status);
|
||||
receivedItems = status.Items;
|
||||
return status.Result;
|
||||
});
|
||||
ServiceTrace.LogSuspend("GetStatus exit with {0}", archestrAResult.Success ? "success" : "failure");
|
||||
ServiceTrace.LogCsv("GetStatus exit [success]", archestrAResult.Success);
|
||||
items = receivedItems;
|
||||
return archestrAResult;
|
||||
}
|
||||
|
||||
public void OnDisconnect()
|
||||
{
|
||||
ServiceTrace.LogCsv("OnDisconnect entry");
|
||||
ServiceTrace.LogResume("OnDisconnect entry");
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected ArchestrAResult PrepareToSend(ConnectedRequest request, Func<ConnectedRequest, T, ArchestrAResult> processFunc)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return ArchestrAResult.MakeResult(ArchestrAError.BadNoCommunication, 0).AddErrorMessage("No request message provided for signature");
|
||||
}
|
||||
if (processFunc == null)
|
||||
{
|
||||
return ArchestrAResult.MakeResult(ArchestrAError.BadNoCommunication, 0).AddErrorMessage("No processing function provided");
|
||||
}
|
||||
ArchestrAResult archestrAResult = SignRequest(request);
|
||||
if (!archestrAResult.Success)
|
||||
{
|
||||
return archestrAResult;
|
||||
}
|
||||
T channelClient = ChannelClient;
|
||||
if (channelClient == null)
|
||||
{
|
||||
return ArchestrAResult.MakeResult(ArchestrAError.BadNoCommunication, 0).AddErrorMessage("No connected client for the current connection Id");
|
||||
}
|
||||
return processFunc(request, channelClient);
|
||||
}
|
||||
|
||||
protected ArchestrAResult SignRequest(ConnectedRequest request)
|
||||
{
|
||||
ArchestrAResult result = ArchestrAResult.MakeResult(ArchestrAError.InvalidConnectionId, 0);
|
||||
if (successfulConnectionResult != null && successfulConnectionResult.ServiceClient != null && State != CommunicationState.Faulted)
|
||||
{
|
||||
SystemAuthenticationClientAuthentication clientAuthenticator = SysAuthenticatorClientCache.GetClientAuthenticator(successfulConnectionResult.ConnectionId);
|
||||
if (clientAuthenticator != null)
|
||||
{
|
||||
clientAuthenticator.Sign(request, forceHmac: false);
|
||||
return ArchestrAResult.MakeGoodResult();
|
||||
}
|
||||
result.AddErrorMessage("Unable to find a signer using the current connection Id");
|
||||
}
|
||||
else
|
||||
{
|
||||
result.AddErrorMessage("Unable to use the client proxy for the current connection Id");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void UpdateDownstreamConnectionState(ConnectedResponse response)
|
||||
{
|
||||
if (response != null)
|
||||
{
|
||||
if (response.DownstreamConnectionState <= 6)
|
||||
{
|
||||
DownstreamConnectionState = (ConnectionState)response.DownstreamConnectionState;
|
||||
return;
|
||||
}
|
||||
ServiceTrace.LogWarning(string.Format(CultureInfo.InvariantCulture, "ASB Base V2 client failed to parse downstream connection state value of {0}", new object[1] { response.DownstreamConnectionState }));
|
||||
DownstreamConnectionState = ConnectionState.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
~BaseV2Client()
|
||||
{
|
||||
Dispose(disposing: false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (disposing)
|
||||
{
|
||||
if (successfulConnectionResult != null)
|
||||
{
|
||||
successfulConnectionResult.Dispose();
|
||||
}
|
||||
successfulConnectionResult = null;
|
||||
}
|
||||
disposed = true;
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,492 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.ServiceModel;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using ArchestrAServices.Common;
|
||||
using Asb.Base.V2.ContractTypes;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
|
||||
public abstract class BaseV2Shim : IAuthenticateAsb
|
||||
{
|
||||
private sealed class ConnectionContext : IDisposable
|
||||
{
|
||||
private readonly TimeSpan keepAliveTimeout;
|
||||
|
||||
private readonly Timer timer;
|
||||
|
||||
private bool disposed;
|
||||
|
||||
public IContextChannel ConnectionChannel { get; private set; }
|
||||
|
||||
public IBaseV2 ConnectionImplementation { get; }
|
||||
|
||||
public ConnectionContext(IBaseV2 implementation, TimerCallback keepAliveTimeoutCallback, Guid connectionId, TimeSpan keepAliveTimeout)
|
||||
{
|
||||
ConnectionChannel = ((OperationContext.Current == null) ? null : OperationContext.Current.Channel);
|
||||
ConnectionImplementation = implementation;
|
||||
timer = new Timer(keepAliveTimeoutCallback, connectionId, (int)keepAliveTimeout.TotalMilliseconds, -1);
|
||||
this.keepAliveTimeout = keepAliveTimeout;
|
||||
}
|
||||
|
||||
~ConnectionContext()
|
||||
{
|
||||
Dispose(disposing: false);
|
||||
}
|
||||
|
||||
public void RestartTimer()
|
||||
{
|
||||
timer.Change((int)keepAliveTimeout.TotalMilliseconds, -1);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
disposed = true;
|
||||
if (disposing)
|
||||
{
|
||||
if (ConnectionChannel != null)
|
||||
{
|
||||
ConnectionChannel.Close();
|
||||
ConnectionChannel = null;
|
||||
}
|
||||
if (timer != null)
|
||||
{
|
||||
timer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ReaderWriterLockSlim implementationLock = new ReaderWriterLockSlim();
|
||||
|
||||
private static readonly Dictionary<Guid, ConnectionContext> implementations = new Dictionary<Guid, ConnectionContext>();
|
||||
|
||||
private static readonly TimeSpan defaultKeepAliveTimeout = TimeSpan.FromSeconds(30.0);
|
||||
|
||||
public virtual ConnectionState DownstreamConnectionState { get; set; }
|
||||
|
||||
protected virtual TimeSpan KeepAliveTimeout => defaultKeepAliveTimeout;
|
||||
|
||||
public ConnectResponse Connect(ConnectRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ServiceTrace.LogCsv("Connect entry [ConId]", request.ConnectionId);
|
||||
ServiceTrace.LogResume("Connect entry, connection Id {0} ", request.ConnectionId);
|
||||
ConnectResponse connectResponse = SystemAuthenticationServiceAuthentication.ProcessClientConnection(request);
|
||||
ServiceTrace.LogVerbose("BaseV2Shim.Connect started new SysAuthenticator for connection Id {0}", connectResponse.ConnectionValidator.ConnectionId);
|
||||
ServiceTrace.LogSuspend("Connect exit, connection Id {0}, success = {1}", request.ConnectionId, connectResponse.Result.Success);
|
||||
ServiceTrace.LogCsv("Connect exit [ConId Success]", request.ConnectionId, connectResponse.Result.Success);
|
||||
return connectResponse;
|
||||
}
|
||||
|
||||
public AuthenticateMeResponse AuthenticateMe(AuthenticateMeRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ServiceTrace.LogCsv("AuthenticateMe entry [ConnId]", request.ConnectionValidator.ConnectionId);
|
||||
ServiceTrace.LogResume("AuthenticateMe entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
|
||||
ArchestrAResult archestrAResult = ArchestrAResult.MakeResult(ArchestrAError.OperationFailed, 0);
|
||||
SystemAuthenticationServiceAuthentication serviceAuthenticator = SysAuthenticatorServiceCache.GetServiceAuthenticator(request.ConnectionValidator.ConnectionId);
|
||||
if (serviceAuthenticator != null)
|
||||
{
|
||||
if (serviceAuthenticator.ProcessClientAuthenticateMe(request))
|
||||
{
|
||||
ServiceTrace.LogVerbose("AuthenticateMe found authenticator and validated request message, establishing serivce implementation for connect Id {0})", serviceAuthenticator.ConnectionId);
|
||||
ConsumerMetadata metadata = ConsumerMetadata.Create(request.ConsumerMetaData);
|
||||
archestrAResult = CheckConsumerMetadata(metadata, serviceAuthenticator.ConnectionId);
|
||||
if (archestrAResult.Success)
|
||||
{
|
||||
archestrAResult = CreateServiceImplmentation(serviceAuthenticator, metadata);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
archestrAResult = ArchestrAResult.MakeResult(ArchestrAError.ApplicationAuthenticationError, 0);
|
||||
string message = string.Format(CultureInfo.CurrentCulture, "AuthenticateMe unable to authenticate client for ConnectionId {0}", new object[1] { serviceAuthenticator.ConnectionId });
|
||||
ServiceTrace.LogWarning(message);
|
||||
archestrAResult.AddErrorMessage(message);
|
||||
}
|
||||
}
|
||||
if (!archestrAResult.Success)
|
||||
{
|
||||
OnDisconnect(request.ConnectionValidator.ConnectionId);
|
||||
}
|
||||
ServiceTrace.LogSuspend("AuthenticateMe exit, connection Id {0}, success = {1}", request.ConnectionValidator.ConnectionId, archestrAResult.Success);
|
||||
ServiceTrace.LogCsv("AuthenticateMe exit [ConId Success]", request.ConnectionValidator.ConnectionId, archestrAResult.Success);
|
||||
return new AuthenticateMeResponse(archestrAResult, DownstreamConnectionState);
|
||||
}
|
||||
|
||||
public KeepAliveResponse KeepAlive(KeepAliveRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ServiceTrace.LogCsv("KeepAlive entry [ConnId]", request.ConnectionValidator.ConnectionId);
|
||||
ServiceTrace.LogResume("KeepAlive entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
|
||||
ArchestrAResult result = ArchestrAResult.MakeResult(ArchestrAError.OperationFailed, 0);
|
||||
SystemAuthenticationServiceAuthentication serviceAuthenticator = SysAuthenticatorServiceCache.GetServiceAuthenticator(request.ConnectionValidator.ConnectionId);
|
||||
if (serviceAuthenticator != null && serviceAuthenticator.ValidRequest(request, forceHmac: false))
|
||||
{
|
||||
implementationLock.EnterReadLock();
|
||||
bool flag;
|
||||
ConnectionContext value;
|
||||
try
|
||||
{
|
||||
flag = implementations.TryGetValue(request.ConnectionValidator.ConnectionId, out value);
|
||||
}
|
||||
finally
|
||||
{
|
||||
implementationLock.ExitReadLock();
|
||||
}
|
||||
if (flag && value != null)
|
||||
{
|
||||
value.RestartTimer();
|
||||
IBaseV2 connectionImplementation = value.ConnectionImplementation;
|
||||
try
|
||||
{
|
||||
result = EnsureValidResult(connectionImplementation.KeepAlive());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServiceTrace.LogWarning("KeepAlive caught exception from implementation for connection Id {0}: {1}", request.ConnectionValidator.ConnectionId, ex.Message);
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
ServiceTrace.LogWarning(" {0}", ex.InnerException.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ServiceTrace.LogResume("KeepAlive exit, connection Id {0}", request.ConnectionValidator.ConnectionId);
|
||||
ServiceTrace.LogResume("KeepAlive exit [ConId]", request.ConnectionValidator.ConnectionId);
|
||||
return new KeepAliveResponse(result, DownstreamConnectionState);
|
||||
}
|
||||
|
||||
public GetStatusItemsResponse GetStatusItems(GetStatusItemsRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ServiceTrace.LogCsv("GetStatusItems entry [ConnId]", request.ConnectionValidator.ConnectionId);
|
||||
ServiceTrace.LogResume("GetStatusItems entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
|
||||
GetStatusItemsResponse getStatusItemsResponse = new GetStatusItemsResponse(ArchestrAResult.MakeResult(ArchestrAError.InvalidConnectionId, 0), null, DownstreamConnectionState);
|
||||
IBaseV2 implementation = GetImplementation<IBaseV2>(request);
|
||||
if (implementation != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
getStatusItemsResponse.Result = EnsureValidResult(implementation.GetStatusItems(out var items));
|
||||
List<string> list = items.ToList();
|
||||
list.Add("ServiceLoadTime");
|
||||
getStatusItemsResponse.Items = list.ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServiceTrace.LogWarning("GetStatusItems caught exception from implementation for ConnectionId {0}: {1}", request.ConnectionValidator.ConnectionId, ex.Message);
|
||||
getStatusItemsResponse.Result.AddErrorMessage(ex.Message);
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
ServiceTrace.LogWarning(" {0}", ex.InnerException.Message);
|
||||
getStatusItemsResponse.Result.AddErrorMessage(ex.InnerException.Message);
|
||||
}
|
||||
getStatusItemsResponse.Result.ResultCodeAsError = ArchestrAError.OperationFailed;
|
||||
}
|
||||
}
|
||||
ServiceTrace.LogSuspend("GetStatusItems exit, connection Id {0}, success = {1}", request.ConnectionValidator.ConnectionId, getStatusItemsResponse.Result.Success);
|
||||
ServiceTrace.LogCsv("GetStatusItems exit [ConId Success]", request.ConnectionValidator.ConnectionId, getStatusItemsResponse.Result.Success);
|
||||
return getStatusItemsResponse;
|
||||
}
|
||||
|
||||
public GetStatusResponse GetStatus(GetStatusRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ServiceTrace.LogCsv("GetStatus entry [ConnId]", request.ConnectionValidator.ConnectionId);
|
||||
ServiceTrace.LogResume("GetStatus entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
|
||||
GetStatusResponse getStatusResponse = new GetStatusResponse(ArchestrAResult.MakeResult(ArchestrAError.InvalidConnectionId, 0), null, DownstreamConnectionState);
|
||||
IBaseV2 implementation = GetImplementation<IBaseV2>(request);
|
||||
if (implementation != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
getStatusResponse.Result = EnsureValidResult(implementation.GetStatus(request.ItemsToReturn, out var items));
|
||||
List<NamedValue> list = items.ToList();
|
||||
list.Add(new NamedValue
|
||||
{
|
||||
Name = "ServiceLoadTime",
|
||||
Value = Value.Create(implementation.ServiceLoadTime)
|
||||
});
|
||||
getStatusResponse.Items = list.ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServiceTrace.LogWarning("GetStatus caught exception from implementation for ConnectionId {0}: {1}", request.ConnectionValidator.ConnectionId, ex.Message);
|
||||
getStatusResponse.Result.AddErrorMessage(ex.Message);
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
ServiceTrace.LogWarning(" {0}", ex.InnerException.Message);
|
||||
getStatusResponse.Result.AddErrorMessage(ex.InnerException.Message);
|
||||
}
|
||||
getStatusResponse.Result.ResultCodeAsError = ArchestrAError.OperationFailed;
|
||||
}
|
||||
}
|
||||
ServiceTrace.LogSuspend("GetStatus exit, connection Id {0}, success = {1}", request.ConnectionValidator.ConnectionId, getStatusResponse.Result.Success);
|
||||
ServiceTrace.LogCsv("GetStatus exit [ConId Success]", request.ConnectionValidator.ConnectionId, getStatusResponse.Result.Success);
|
||||
return getStatusResponse;
|
||||
}
|
||||
|
||||
public RenewResponse Renew(RenewRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ServiceTrace.LogCsv("Renew entry [ConnId]", request.ConnectionValidator.ConnectionId);
|
||||
ServiceTrace.LogResume("Renew entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
|
||||
RenewResponse renewResponse = SystemAuthenticationServiceAuthentication.ProcessClientRenew(request);
|
||||
ServiceTrace.LogSuspend("Renew exit, connection Id {0}, success = {1}", request.ConnectionValidator.ConnectionId, renewResponse.Result.Success);
|
||||
ServiceTrace.LogCsv("Renew exit [ConId Success]", request.ConnectionValidator.ConnectionId, renewResponse.Result.Success);
|
||||
return renewResponse;
|
||||
}
|
||||
|
||||
public void UpdateSystemAuthenticationConfiguration(UpdateSystemAuthenticationConfigurationRequest request)
|
||||
{
|
||||
if (request != null)
|
||||
{
|
||||
ServiceTrace.LogCsv("UpdateSystemAuthenticationConfiguration entry [ConnId]", request.ConnectionValidator.ConnectionId);
|
||||
ServiceTrace.LogResume("UpdateSystemAuthenticationConfiguration entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
|
||||
SystemAuthenticationServiceAuthentication.ProcessClientUpdateSystemAuthenticationConfiguration(request);
|
||||
ServiceTrace.LogSuspend("UpdateSystemAuthenticationConfiguration exit, connection Id {0}", request.ConnectionValidator.ConnectionId);
|
||||
ServiceTrace.LogCsv("UpdateSystemAuthenticationConfiguration exit [ConId]", request.ConnectionValidator.ConnectionId);
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect(DisconnectRequest request)
|
||||
{
|
||||
if (request != null)
|
||||
{
|
||||
ServiceTrace.LogCsv("Disconnect entry [ConnId]", request.ConnectionValidator.ConnectionId);
|
||||
ServiceTrace.LogResume("Disconnect entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
|
||||
SystemAuthenticationServiceAuthentication serviceAuthenticator = SysAuthenticatorServiceCache.GetServiceAuthenticator(request.ConnectionValidator.ConnectionId);
|
||||
if (serviceAuthenticator != null && serviceAuthenticator.ValidRequest(request, forceHmac: false))
|
||||
{
|
||||
OnDisconnect(request.ConnectionValidator.ConnectionId);
|
||||
serviceAuthenticator.ProcessClientDisconnect(request);
|
||||
}
|
||||
ServiceTrace.LogSuspend("Disconnect exit, connection Id {0}", request.ConnectionValidator.ConnectionId);
|
||||
ServiceTrace.LogCsv("Disconnect exit [ConId]", request.ConnectionValidator.ConnectionId);
|
||||
}
|
||||
}
|
||||
|
||||
protected UserToken DecryptUserToken(Guid connectionId, UserTokenContract wireToken)
|
||||
{
|
||||
if (wireToken == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
SystemAuthenticationServiceAuthentication serviceAuthenticator = SysAuthenticatorServiceCache.GetServiceAuthenticator(connectionId);
|
||||
if (serviceAuthenticator == null || !serviceAuthenticator.SecureSessionEstablished)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot decrypt user token due to session in wrong state");
|
||||
}
|
||||
UserToken userToken = new UserToken
|
||||
{
|
||||
Encryption = wireToken.Encryption,
|
||||
HostName = wireToken.HostName,
|
||||
IdType = wireToken.IdType,
|
||||
LocationId = wireToken.LocationId,
|
||||
UserName = wireToken.UserName,
|
||||
Validity = wireToken.Validity
|
||||
};
|
||||
if (wireToken.Password != null)
|
||||
{
|
||||
byte[] bytes = serviceAuthenticator.Decypher(wireToken.Password, serviceAuthenticator.EncryptionKey);
|
||||
userToken.Password = Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
if (wireToken.SamlToken != null)
|
||||
{
|
||||
userToken.SamlToken = serviceAuthenticator.Decypher(wireToken.SamlToken, serviceAuthenticator.EncryptionKey);
|
||||
}
|
||||
if (wireToken.JwtToken != null)
|
||||
{
|
||||
byte[] bytes2 = serviceAuthenticator.Decypher(wireToken.JwtToken, serviceAuthenticator.EncryptionKey);
|
||||
userToken.JwtToken = Encoding.UTF8.GetString(bytes2);
|
||||
}
|
||||
if (wireToken.X509Certificate != null)
|
||||
{
|
||||
userToken.X509Certificate = serviceAuthenticator.Decypher(wireToken.X509Certificate, serviceAuthenticator.EncryptionKey);
|
||||
}
|
||||
return userToken;
|
||||
}
|
||||
|
||||
protected static ArchestrAResult EnsureValidResult(ArchestrAResult result)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return ArchestrAResult.MakeErrorResult("Service returned no result");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private ArchestrAResult CreateServiceImplmentation(SystemAuthenticationServiceAuthentication authenticator, ConsumerMetadata metadata)
|
||||
{
|
||||
ArchestrAResult result = ArchestrAResult.MakeGoodResult();
|
||||
IBaseV2 implementation = GetImplementation();
|
||||
if (implementation == null)
|
||||
{
|
||||
result = ArchestrAResult.MakeResult(ArchestrAError.Unknown, 0);
|
||||
string message = string.Format(CultureInfo.CurrentCulture, "AuthenticateMe could not create a service implementation object for ConnectionId {0}", new object[1] { authenticator.ConnectionId });
|
||||
ServiceTrace.LogWarning(message);
|
||||
result.AddErrorMessage(message);
|
||||
return result;
|
||||
}
|
||||
try
|
||||
{
|
||||
ServiceTrace.LogCsv("AuthenticateMe found authenticator and validated request message, calling implementation OnConnect [ConId]", authenticator.ConnectionId);
|
||||
ServiceTrace.LogVerbose("AuthenticateMe found authenticator and validated request message, calling implementation OnConnect for connection Id {0}", authenticator.ConnectionId);
|
||||
implementation.ServiceLoadTime = DateTime.UtcNow;
|
||||
implementation.ConnectionId = authenticator.ConnectionId;
|
||||
implementation.OnConnect(authenticator.Lifetime, metadata);
|
||||
CreateConnectionContext(authenticator.ConnectionId, implementation);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = ArchestrAResult.MakeResult(ArchestrAError.Unknown, 0);
|
||||
ServiceTrace.LogWarning("AuthenticateMe caught exception from implementation for ConnectionId {0}: {1}", authenticator.ConnectionId, ex.Message);
|
||||
result.AddErrorMessage(ex.Message);
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
ServiceTrace.LogWarning(" {0}", ex.InnerException.Message);
|
||||
result.AddErrorMessage(ex.InnerException.Message);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void CreateConnectionContext(Guid connectionId, IBaseV2 implementation)
|
||||
{
|
||||
implementationLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
implementations.Add(connectionId, new ConnectionContext(implementation, KeepaliveHandler, connectionId, KeepAliveTimeout));
|
||||
ServiceTrace.LogVerbose("AuthenticateMe added implementation for connection Id {0}", connectionId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
implementationLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnDisconnect(Guid connectionId)
|
||||
{
|
||||
IBaseV2 baseV = null;
|
||||
bool flag = false;
|
||||
implementationLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
flag = implementations.TryGetValue(connectionId, out var value);
|
||||
if (flag)
|
||||
{
|
||||
implementations.Remove(connectionId);
|
||||
ServiceTrace.LogVerbose("OnDisconnect removed implementation for ConnectionId {0}", connectionId);
|
||||
baseV = value.ConnectionImplementation;
|
||||
value.Dispose();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServiceTrace.LogVerbose("OnDisconnect handled an exception: {0}", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
implementationLock.ExitWriteLock();
|
||||
}
|
||||
if (flag && baseV != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
ServiceTrace.LogCsv("OnDisconnect found authenticator and validated request message, calling implementation OnDisconnect Id =", connectionId);
|
||||
baseV.OnDisconnect();
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
ServiceTrace.LogWarning("OnDisconnect caught exception from implementation for ConnectionId {0}: {1}", connectionId, ex2.Message);
|
||||
if (ex2.InnerException != null)
|
||||
{
|
||||
ServiceTrace.LogWarning(" {0}", ex2.InnerException.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
SysAuthenticatorServiceCache.RemoveServiceAuthenticator(connectionId);
|
||||
}
|
||||
|
||||
private static void KeepaliveHandler(object connectionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
OnDisconnect((Guid)connectionId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServiceTrace.LogVerbose("KeepaliveHandler handled an exception {0}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual ArchestrAResult CheckConsumerMetadata(ConsumerMetadata metadata, Guid connectionId)
|
||||
{
|
||||
return ArchestrAResult.MakeGoodResult();
|
||||
}
|
||||
|
||||
protected abstract IBaseV2 GetImplementation();
|
||||
|
||||
protected static T GetImplementation<T>(ConnectedRequest request) where T : class, IBaseV2
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
SystemAuthenticationServiceAuthentication serviceAuthenticator = SysAuthenticatorServiceCache.GetServiceAuthenticator(request.ConnectionValidator.ConnectionId);
|
||||
if (serviceAuthenticator != null && serviceAuthenticator.ValidRequest(request, forceHmac: false))
|
||||
{
|
||||
implementationLock.EnterReadLock();
|
||||
bool flag;
|
||||
ConnectionContext value;
|
||||
try
|
||||
{
|
||||
flag = implementations.TryGetValue(request.ConnectionValidator.ConnectionId, out value);
|
||||
}
|
||||
finally
|
||||
{
|
||||
implementationLock.ExitReadLock();
|
||||
}
|
||||
if (flag && value != null)
|
||||
{
|
||||
value.RestartTimer();
|
||||
return value.ConnectionImplementation as T;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[Serializable]
|
||||
[XmlType(Namespace = "urn:data.asb.se:2", IncludeInSchema = false)]
|
||||
public enum ClientAccess
|
||||
{
|
||||
FullAccess,
|
||||
ReadOnly
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Discovery;
|
||||
using System.ServiceModel.Security;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using ArchestrAServices.Common;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
internal class ClientManagement : IClientManagement
|
||||
{
|
||||
public ConnectContext<T> CreateConnectContext<T>(EndpointAddress serviceEndpointAddress, Binding binding, string application, string user, CancellationToken cancellationToken) where T : class, IAuthenticateAsb
|
||||
{
|
||||
ConnectContext<T> connectContext = new ConnectContext<T>
|
||||
{
|
||||
Success = false,
|
||||
ErrorMessage = string.Empty,
|
||||
ServiceChannelFactory = null,
|
||||
ServiceClient = null,
|
||||
ServiceChannel = null,
|
||||
ConnectionId = Guid.Empty,
|
||||
ConnectionUser = user,
|
||||
ConnectionApplication = application
|
||||
};
|
||||
ServiceTrace.LogVerbose("Try connecting with endpoint {0}", serviceEndpointAddress.Uri.AbsoluteUri);
|
||||
connectContext.ServiceChannelFactory = new ChannelFactory<T>(binding, serviceEndpointAddress);
|
||||
if (binding is NetTcpBinding netTcpBinding && netTcpBinding.Security.Mode == SecurityMode.Transport && connectContext.ServiceChannelFactory.Credentials != null)
|
||||
{
|
||||
connectContext.ServiceChannelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.ChainTrust;
|
||||
connectContext.ServiceChannelFactory.Credentials.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;
|
||||
}
|
||||
InjectCustomBehavior(connectContext.ServiceChannelFactory);
|
||||
if (!OpenOrAbort(connectContext.ServiceChannelFactory, cancellationToken))
|
||||
{
|
||||
ServiceTrace.LogVerbose("BaseV2Client CreateConnectContext is cancelled when create channel for {0}", serviceEndpointAddress.Uri.AbsoluteUri);
|
||||
return connectContext;
|
||||
}
|
||||
connectContext.ServiceClient = connectContext.ServiceChannelFactory.CreateChannel();
|
||||
if (connectContext.ServiceClient == null)
|
||||
{
|
||||
ServiceTrace.LogWarning("BaseV2Client not able to create a client for the endpoint {0}", serviceEndpointAddress.Uri.AbsoluteUri);
|
||||
return null;
|
||||
}
|
||||
connectContext.CastClientToChannel();
|
||||
if (connectContext.ServiceChannel == null)
|
||||
{
|
||||
ServiceTrace.LogWarning("BaseV2Client not able to create a channel for the endpoint {0}", serviceEndpointAddress.Uri.AbsoluteUri);
|
||||
return null;
|
||||
}
|
||||
if (!OpenOrAbort(connectContext.ServiceChannel, cancellationToken))
|
||||
{
|
||||
ServiceTrace.LogVerbose("BaseV2Client CreateConnectContext is cancelled when open channel for {0}", serviceEndpointAddress.Uri.AbsoluteUri);
|
||||
}
|
||||
return connectContext;
|
||||
}
|
||||
|
||||
public virtual void InjectCustomBehavior<T>(ChannelFactory<T> channelFactory) where T : class, IAuthenticateAsb
|
||||
{
|
||||
}
|
||||
|
||||
public bool EstablishSecureSession<T>(ConnectContext<T> context, string solutionName, ClientAccess access) where T : class, IAuthenticateAsb
|
||||
{
|
||||
if (context?.ServiceChannel == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (context.ServiceChannel.State != CommunicationState.Faulted)
|
||||
{
|
||||
ServiceTrace.LogVerbose("Channel with endpoint {0} is open, establishing secure session using solution {1}", context.ServiceChannel.RemoteAddress.Uri.AbsoluteUri, solutionName);
|
||||
return context.EstablishSecureSession(solutionName, access);
|
||||
}
|
||||
ServiceTrace.LogVerbose("Secure connection with endpoint {0} is NOT established using solution {1}", context.ServiceChannel.RemoteAddress.Uri.AbsoluteUri, solutionName);
|
||||
context.ServiceChannel.Abort();
|
||||
context.Dispose();
|
||||
if (string.IsNullOrEmpty(context.ErrorMessage))
|
||||
{
|
||||
context.ErrorMessage = "BaseV2Client could not connect with service: EstablishSecureSession failed";
|
||||
}
|
||||
ServiceTrace.LogWarning(context.ErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void DisconnectSecureSession<T>(ConnectContext<T> context) where T : class, IAuthenticateAsb
|
||||
{
|
||||
context?.DisconnectSecureSession();
|
||||
}
|
||||
|
||||
public IEnumerable<EndpointDiscoveryMetadata> DiscoverEndpointsForContract(XmlQualifiedName contract, string scopeRule)
|
||||
{
|
||||
if (contract == null)
|
||||
{
|
||||
throw new ArgumentNullException("contract");
|
||||
}
|
||||
ServiceTrace.LogCsv("DiscoverEndpointsForContract entry [Contract scopeRule]", contract, string.IsNullOrEmpty(scopeRule) ? "<empty>" : scopeRule);
|
||||
ServiceTrace.LogResume("DiscoverEndpointsForContract entry, contract {0}, scopeRule {1}", contract, string.IsNullOrEmpty(scopeRule) ? "<empty>" : scopeRule);
|
||||
FindResponse findResponse;
|
||||
try
|
||||
{
|
||||
Uri uri = RegistryHandler.MakeLDSProbeEndpointAddress(Environment.MachineName);
|
||||
ServiceTrace.LogVerbose("DiscoverEndpointsForContract({0}) generated LDS endpoint for localhost: {1}", contract, uri.AbsoluteUri);
|
||||
FindCriteria findCriteria = new FindCriteria();
|
||||
findCriteria.ContractTypeNames.Add(contract);
|
||||
List<string> list = new List<string>();
|
||||
if (!string.IsNullOrEmpty(scopeRule))
|
||||
{
|
||||
list.Add(scopeRule);
|
||||
}
|
||||
Collection<Uri> collection = SvcUtilities.CreateFindScopes(string.Empty, string.Empty, string.Empty, list);
|
||||
ServiceTrace.LogVerbose("DiscoverEndpointsForContract({0}) generated {1} scope Uris:", contract, collection.Count);
|
||||
findCriteria.Scopes.Clear();
|
||||
foreach (Uri item in collection)
|
||||
{
|
||||
ServiceTrace.LogVerbose(" {0}", item.AbsoluteUri);
|
||||
findCriteria.Scopes.Add(item);
|
||||
}
|
||||
using DiscoveryClient discoveryClient = new DiscoveryClient(new DiscoveryEndpoint(SvcUtilities.GetBinding(uri.AbsoluteUri), new EndpointAddress(uri)));
|
||||
findResponse = discoveryClient.Find(findCriteria);
|
||||
ServiceTrace.LogVerbose("DiscoverEndpointsForContract({0}) found {1} endpoints", contract, findResponse.Endpoints.Count);
|
||||
}
|
||||
catch (TargetInvocationException ex)
|
||||
{
|
||||
ServiceTrace.LogError("DiscoverEndpointsForContract({0}, {1}) TargetInvocationException: {2}", contract, scopeRule, ex.Message);
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
ServiceTrace.LogError(" {0}", ex.InnerException.Message);
|
||||
}
|
||||
findResponse = null;
|
||||
}
|
||||
catch (ObjectDisposedException ex2)
|
||||
{
|
||||
ServiceTrace.LogError("DiscoverEndpointsForContract({0}, {1}) ObjectDisposedException: {2}", contract, scopeRule, ex2.Message);
|
||||
if (ex2.InnerException != null)
|
||||
{
|
||||
ServiceTrace.LogError(" {0}", ex2.InnerException.Message);
|
||||
}
|
||||
findResponse = null;
|
||||
}
|
||||
catch (Exception ex3)
|
||||
{
|
||||
ServiceTrace.LogError("DiscoverEndpointsForContract({0}, {1}) Exception: {2}", contract, scopeRule, ex3.Message);
|
||||
if (ex3.InnerException != null)
|
||||
{
|
||||
ServiceTrace.LogError(" {0}", ex3.InnerException.Message);
|
||||
}
|
||||
findResponse = null;
|
||||
}
|
||||
ServiceTrace.LogSuspend("DiscoverEndpointsForContract exit, contract {0}, scopeRule {1}", contract, string.IsNullOrEmpty(scopeRule) ? "<empty>" : scopeRule);
|
||||
ServiceTrace.LogCsv("DiscoverEndpointsForContract exit [Contract scopeRule]", contract, string.IsNullOrEmpty(scopeRule) ? "<empty>" : scopeRule);
|
||||
return findResponse?.Endpoints;
|
||||
}
|
||||
|
||||
private static bool OpenOrAbort(ICommunicationObject communicationObject, CancellationToken cancellationToken)
|
||||
{
|
||||
Task task = Task.Factory.StartNew(communicationObject.Open, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
|
||||
try
|
||||
{
|
||||
task.Wait(cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
communicationObject.Abort();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[Serializable]
|
||||
[XmlType(Namespace = "urn:asb.se:2")]
|
||||
public class ClientMetadata
|
||||
{
|
||||
[XmlElement(ElementName = "UserName", Order = 0)]
|
||||
public string UserName { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "HostName", Order = 1)]
|
||||
public string HostName { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "SessionId", Order = 2)]
|
||||
public string SessionId { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "SessionHostName", Order = 3)]
|
||||
public string SessionHostName { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "ApplicationName", Order = 4)]
|
||||
public string ApplicationName { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "Access", Order = 5)]
|
||||
public ClientAccess Access { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.ServiceModel;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public sealed class ConnectContext<T> : IDisposable where T : class, IAuthenticateAsb
|
||||
{
|
||||
private bool disposed;
|
||||
|
||||
public bool Success { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
public ChannelFactory<T> ServiceChannelFactory { get; set; }
|
||||
|
||||
public T ServiceClient { get; set; }
|
||||
|
||||
public IClientChannel ServiceChannel { get; set; }
|
||||
|
||||
public Guid ConnectionId { get; set; }
|
||||
|
||||
public string ConnectionUser { get; set; }
|
||||
|
||||
public string ConnectionApplication { get; set; }
|
||||
|
||||
~ConnectContext()
|
||||
{
|
||||
Dispose(disposing: false);
|
||||
}
|
||||
|
||||
public bool EstablishSecureSession(string solutionName, ClientAccess access)
|
||||
{
|
||||
string errorMessage = string.Empty;
|
||||
Guid connectId = Guid.Empty;
|
||||
Success = SystemAuthenticationClientAuthentication.EstablishSecureSession(solutionName, GenerateClientMetadata(access), (ConnectRequest request) => ServiceClient.Connect(request), (AuthenticateMeRequest request) => ServiceClient.AuthenticateMe(request), delegate(Guid id)
|
||||
{
|
||||
connectId = id;
|
||||
}, delegate(string msg)
|
||||
{
|
||||
errorMessage = msg;
|
||||
});
|
||||
if (Success)
|
||||
{
|
||||
ConnectionId = connectId;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorMessage = errorMessage;
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
public void DisconnectSecureSession()
|
||||
{
|
||||
SystemAuthenticationClientAuthentication.DisconnectSecureSession(ConnectionId, delegate(DisconnectRequest request)
|
||||
{
|
||||
ServiceClient.Disconnect(request);
|
||||
});
|
||||
}
|
||||
|
||||
public void CastClientToChannel()
|
||||
{
|
||||
ServiceChannel = ServiceClient as IClientChannel;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private ClientMetadata GenerateClientMetadata(ClientAccess access)
|
||||
{
|
||||
ClientMetadata clientMetadata = new ClientMetadata
|
||||
{
|
||||
UserName = (string.IsNullOrEmpty(ConnectionUser) ? Environment.UserName : ConnectionUser),
|
||||
HostName = Environment.MachineName,
|
||||
ApplicationName = (string.IsNullOrEmpty(ConnectionApplication) ? Process.GetCurrentProcess().ProcessName : ConnectionApplication),
|
||||
Access = access
|
||||
};
|
||||
string terminalServicesClientName = GetTerminalServicesClientName();
|
||||
if (terminalServicesClientName == "\0")
|
||||
{
|
||||
clientMetadata.SessionHostName = string.Empty;
|
||||
clientMetadata.SessionId = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
clientMetadata.SessionHostName = terminalServicesClientName;
|
||||
clientMetadata.SessionId = Process.GetCurrentProcess().SessionId.ToString();
|
||||
}
|
||||
return clientMetadata;
|
||||
}
|
||||
|
||||
private static string GetTerminalServicesClientName()
|
||||
{
|
||||
IntPtr ppBuffer;
|
||||
int pBytesReturned;
|
||||
bool num = NativeMethods.WTSQuerySessionInformation(NativeMethods.WTS_CURRENT_SERVER_HANDLE, -1, NativeMethods.WTS_INFO_CLASS.WTSClientName, out ppBuffer, out pBytesReturned);
|
||||
string result = null;
|
||||
if (num)
|
||||
{
|
||||
result = Marshal.PtrToStringAuto(ppBuffer);
|
||||
NativeMethods.WTSFreeMemory(ppBuffer);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
disposed = true;
|
||||
if (disposing)
|
||||
{
|
||||
if (ServiceChannel != null)
|
||||
{
|
||||
ServiceChannel.Close();
|
||||
ServiceChannel.Dispose();
|
||||
}
|
||||
ServiceChannel = null;
|
||||
if (ServiceChannelFactory != null)
|
||||
{
|
||||
ServiceChannelFactory.Close();
|
||||
((IDisposable)ServiceChannelFactory).Dispose();
|
||||
}
|
||||
ServiceChannelFactory = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "Connect", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
[XmlRoot(Namespace = "urn:msg.asb.se:2")]
|
||||
public class ConnectRequest : ServiceMessage
|
||||
{
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 0)]
|
||||
[XmlElement(ElementName = "ConnectionId")]
|
||||
public Guid ConnectionId { get; set; }
|
||||
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 1)]
|
||||
[XmlElement(ElementName = "ConsumerPublicKey")]
|
||||
public PublicKey ConsumerPublicKey { get; set; }
|
||||
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 2)]
|
||||
[XmlElement(ElementName = "SecurityPolicy")]
|
||||
public string SecurityPolicy { get; set; }
|
||||
|
||||
public ConnectRequest()
|
||||
{
|
||||
}
|
||||
|
||||
public ConnectRequest(Guid connectionId, PublicKey consumerPublicKey, string securityPolicy)
|
||||
{
|
||||
ConnectionId = connectionId;
|
||||
ConsumerPublicKey = consumerPublicKey;
|
||||
SecurityPolicy = securityPolicy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "ConnectResponse", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
[XmlRoot(Namespace = "urn:msg.asb.se:2")]
|
||||
public class ConnectResponse : ConnectedResponse
|
||||
{
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 2)]
|
||||
[XmlElement(ElementName = "ServicePublicKey")]
|
||||
public PublicKey ServicePublicKey { get; set; }
|
||||
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 3)]
|
||||
[XmlElement(ElementName = "ServiceAuthenticationData")]
|
||||
public AuthenticationData ServiceAuthenticationData { get; set; }
|
||||
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 4)]
|
||||
[XmlElement(ElementName = "ConnectionLifetime", DataType = "duration")]
|
||||
public string ConnectionLifetime { get; set; }
|
||||
|
||||
public ConnectResponse()
|
||||
{
|
||||
}
|
||||
|
||||
public ConnectResponse(ArchestrAResult result, PublicKey servicePublicKey, AuthenticationData serviceAuthenticationData, string connectionLifetime)
|
||||
: base(result)
|
||||
{
|
||||
ServicePublicKey = servicePublicKey;
|
||||
ServiceAuthenticationData = serviceAuthenticationData;
|
||||
ConnectionLifetime = connectionLifetime;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using System.ServiceModel;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract]
|
||||
public class ConnectedRequest : ServiceMessage
|
||||
{
|
||||
[MessageHeader(Namespace = "urn:hdr.asb.se:2")]
|
||||
public ConnectionValidator ConnectionValidator { get; set; }
|
||||
|
||||
public ConnectedRequest()
|
||||
{
|
||||
ConnectionValidator = new ConnectionValidator();
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract]
|
||||
public class ConnectedResponse : ConnectedRequest
|
||||
{
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 0)]
|
||||
[XmlElement(ElementName = "Result")]
|
||||
public ArchestrAResult Result { get; set; }
|
||||
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 1)]
|
||||
[XmlElement(ElementName = "DownstreamState")]
|
||||
public int DownstreamConnectionState { get; set; }
|
||||
|
||||
public ConnectedResponse()
|
||||
{
|
||||
Result = ArchestrAResult.MakeGoodResult();
|
||||
DownstreamConnectionState = 0;
|
||||
}
|
||||
|
||||
public ConnectedResponse(ArchestrAResult result)
|
||||
: this(result, ConnectionState.Unknown)
|
||||
{
|
||||
}
|
||||
|
||||
public ConnectedResponse(ArchestrAResult result, ConnectionState downstreamConnectionState)
|
||||
{
|
||||
Result = result;
|
||||
DownstreamConnectionState = (int)downstreamConnectionState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public enum ConnectionState
|
||||
{
|
||||
Unknown,
|
||||
Created,
|
||||
Opening,
|
||||
Opened,
|
||||
Closing,
|
||||
Closed,
|
||||
Faulted
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[Serializable]
|
||||
[XmlType(Namespace = "urn:asb.se:2")]
|
||||
public class ConnectionValidator
|
||||
{
|
||||
[XmlElement(ElementName = "ConnectionId", Order = 0)]
|
||||
public Guid ConnectionId { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "MessageNumber", Order = 1)]
|
||||
public ulong MessageNumber { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "MessageAuthenticationCode", DataType = "base64Binary", Order = 2)]
|
||||
public byte[] MessageAuthenticationCode { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "SignatureInitializationVector", DataType = "base64Binary", Order = 3)]
|
||||
public byte[] SignatureInitializationVector { get; set; }
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public class ConsumerMetadata
|
||||
{
|
||||
public string UserName { get; private set; }
|
||||
|
||||
public string HostName { get; private set; }
|
||||
|
||||
public string SessionId { get; private set; }
|
||||
|
||||
public string SessionHostName { get; private set; }
|
||||
|
||||
public string ApplicationName { get; private set; }
|
||||
|
||||
public ClientAccess Access { get; set; }
|
||||
|
||||
public ConsumerMetadata(string userName, string hostName, string sessionId, string sessionHostName, string applicationName, ClientAccess access)
|
||||
{
|
||||
UserName = userName;
|
||||
HostName = hostName;
|
||||
SessionId = sessionId;
|
||||
SessionHostName = sessionHostName;
|
||||
ApplicationName = applicationName;
|
||||
Access = access;
|
||||
}
|
||||
|
||||
public static ConsumerMetadata Create(ClientMetadata metadata)
|
||||
{
|
||||
if (metadata == null)
|
||||
{
|
||||
return new ConsumerMetadata(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, ClientAccess.FullAccess);
|
||||
}
|
||||
return new ConsumerMetadata(metadata.UserName, metadata.HostName, metadata.SessionId, metadata.SessionHostName, metadata.ApplicationName, metadata.Access);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public class CustomEnum
|
||||
{
|
||||
public short Ordinal { get; private set; }
|
||||
|
||||
public string OrdinalValue { get; private set; }
|
||||
|
||||
public CustomEnum(short ordinal, string ordinalValue)
|
||||
{
|
||||
Ordinal = ordinal;
|
||||
OrdinalValue = ordinalValue;
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public static class DataConversionUtilities
|
||||
{
|
||||
public static string ToBase64(this string value)
|
||||
{
|
||||
string result = string.Empty;
|
||||
if (value != null)
|
||||
{
|
||||
result = Encoding.UTF8.GetBytes(value).ToBase64();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string FromBase64(this string value)
|
||||
{
|
||||
string result = string.Empty;
|
||||
if (value != null)
|
||||
{
|
||||
byte[] bytes = value.FromBase64ToByteArray();
|
||||
result = Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string ToBase64(this byte[] value)
|
||||
{
|
||||
string result = string.Empty;
|
||||
try
|
||||
{
|
||||
result = Convert.ToBase64String(value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string ToHex(this byte[] value)
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (value != null)
|
||||
{
|
||||
foreach (byte b in value)
|
||||
{
|
||||
stringBuilder.Append(b.ToString("X2", CultureInfo.CurrentCulture));
|
||||
}
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
public static byte[] FromBase64ToByteArray(this string value)
|
||||
{
|
||||
byte[] result = null;
|
||||
if (value != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = Convert.FromBase64String(value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] FromHexToByteArray(this string value)
|
||||
{
|
||||
List<byte> list = new List<byte>();
|
||||
if (value != null)
|
||||
{
|
||||
string text = value.Replace(" ", string.Empty);
|
||||
text = text.Replace("\r", string.Empty);
|
||||
text = text.Replace("\n", string.Empty);
|
||||
if (text.Length % 2 == 0)
|
||||
{
|
||||
for (int i = 0; i < text.Length; i += 2)
|
||||
{
|
||||
if (byte.TryParse(text.Substring(i, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result))
|
||||
{
|
||||
list.Add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "Disconnect", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
public class DisconnectRequest : ConnectedRequest
|
||||
{
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 0)]
|
||||
[XmlElement(ElementName = "ConsumerAuthenticationData")]
|
||||
public AuthenticationData ConsumerAuthenticationData { get; set; }
|
||||
|
||||
public DisconnectRequest()
|
||||
{
|
||||
}
|
||||
|
||||
public DisconnectRequest(AuthenticationData consumerAuthenticationData)
|
||||
{
|
||||
ConsumerAuthenticationData = consumerAuthenticationData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public enum DiscoveryScope
|
||||
{
|
||||
Default,
|
||||
Local,
|
||||
Global,
|
||||
Closest
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[Serializable]
|
||||
[XmlInclude(typeof(SourceHandle))]
|
||||
[XmlInclude(typeof(SolutionHandle))]
|
||||
[XmlType(Namespace = "urn:data.asb.se:2")]
|
||||
public class EntityHandle
|
||||
{
|
||||
[XmlElement(ElementName = "Key", Order = 0)]
|
||||
public Value Key { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "NamePath", Order = 1)]
|
||||
public string NamePath { get; set; }
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "GetStatusItems", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
[XmlRoot(Namespace = "urn:msg.asb.se:2")]
|
||||
public class GetStatusItemsRequest : ConnectedRequest
|
||||
{
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "GetStatusItemsResponse", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
[XmlRoot(Namespace = "urn:msg.asb.se:2")]
|
||||
public class GetStatusItemsResponse : ConnectedResponse
|
||||
{
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 2)]
|
||||
[XmlElement(ElementName = "Items")]
|
||||
public string[] Items { get; set; }
|
||||
|
||||
public GetStatusItemsResponse()
|
||||
{
|
||||
}
|
||||
|
||||
public GetStatusItemsResponse(ArchestrAResult result, string[] items, ConnectionState downstreamConnectionState)
|
||||
: base(result, downstreamConnectionState)
|
||||
{
|
||||
Items = items;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "GetStatus", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
[XmlRoot(Namespace = "urn:msg.asb.se:2")]
|
||||
public class GetStatusRequest : ConnectedRequest
|
||||
{
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 0)]
|
||||
[XmlElement(ElementName = "ItemsToReturn")]
|
||||
public string[] ItemsToReturn { get; set; }
|
||||
|
||||
public GetStatusRequest()
|
||||
{
|
||||
}
|
||||
|
||||
public GetStatusRequest(string[] itemsToReturn)
|
||||
{
|
||||
ItemsToReturn = itemsToReturn;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
using Asb.Base.V2.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "GetStatusResponse", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
[XmlRoot(Namespace = "urn:msg.asb.se:2")]
|
||||
[BaseV2SerializerContractBehavior]
|
||||
public class GetStatusResponse : ConnectedResponse
|
||||
{
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 2)]
|
||||
[XmlElement(ElementName = "Items")]
|
||||
public NamedValue[] Items { get; set; }
|
||||
|
||||
public GetStatusResponse()
|
||||
{
|
||||
}
|
||||
|
||||
public GetStatusResponse(ArchestrAResult result, NamedValue[] items, ConnectionState downstreamConnectionState)
|
||||
: base(result, downstreamConnectionState)
|
||||
{
|
||||
Items = items;
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
using System.ServiceModel;
|
||||
using Asb.Base.V2.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[ServiceContract(Namespace = "urn:asb.se:2", ConfigurationName = "IAuthenticateAsb")]
|
||||
[BaseV2SerializerContractBehavior]
|
||||
public interface IAuthenticateAsb
|
||||
{
|
||||
[OperationContract(Action = "urn:asb.se:2/IAuthenticateAsb/Connect", ReplyAction = "urn:asb.se:2/IAuthenticateAsb/ConnectResponse")]
|
||||
ConnectResponse Connect(ConnectRequest request);
|
||||
|
||||
[OperationContract(Action = "urn:asb.se:2/IAuthenticateAsb/AuthenticateMe", ReplyAction = "urn:asb.se:2/IAuthenticateAsb/AuthenticateMeResponse")]
|
||||
AuthenticateMeResponse AuthenticateMe(AuthenticateMeRequest request);
|
||||
|
||||
[OperationContract(Action = "urn:asb.se:2/IAuthenticateAsb/Renew", ReplyAction = "urn:asb.se:2/IAuthenticateAsb/RenewResponse")]
|
||||
RenewResponse Renew(RenewRequest request);
|
||||
|
||||
[OperationContract(Action = "urn:asb.se:2/IAuthenticateAsb/GetStatusItems", ReplyAction = "urn:asb.se:2/IAuthenticateAsb/GetStatusItemsResponse")]
|
||||
GetStatusItemsResponse GetStatusItems(GetStatusItemsRequest request);
|
||||
|
||||
[OperationContract(Action = "urn:asb.se:2/IAuthenticateAsb/GetStatus", ReplyAction = "urn:asb.se:2/IAuthenticateAsb/GetStatusResponse")]
|
||||
GetStatusResponse GetStatus(GetStatusRequest request);
|
||||
|
||||
[OperationContract(IsOneWay = true, Action = "urn:asb.se:2/IAuthenticateAsb/UpdateSystemAuthenticationConfiguration")]
|
||||
void UpdateSystemAuthenticationConfiguration(UpdateSystemAuthenticationConfigurationRequest request);
|
||||
|
||||
[OperationContract(Action = "urn:asb.se:2/IAuthenticateAsb/KeepAlive")]
|
||||
KeepAliveResponse KeepAlive(KeepAliveRequest request);
|
||||
|
||||
[OperationContract(IsOneWay = true, Action = "urn:asb.se:2/IAuthenticateAsb/Disconnect")]
|
||||
void Disconnect(DisconnectRequest request);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public interface IBaseV2
|
||||
{
|
||||
Guid ConnectionId { get; set; }
|
||||
|
||||
DateTime ServiceLoadTime { get; set; }
|
||||
|
||||
void OnConnect(ulong timeout, ConsumerMetadata metadata);
|
||||
|
||||
ArchestrAResult KeepAlive();
|
||||
|
||||
ArchestrAResult GetStatusItems(out string[] items);
|
||||
|
||||
ArchestrAResult GetStatus(string[] itemsToReturn, out NamedValue[] items);
|
||||
|
||||
void OnDisconnect();
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Discovery;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
internal interface IClientManagement
|
||||
{
|
||||
ConnectContext<T> CreateConnectContext<T>(EndpointAddress serviceEndpointAddress, Binding binding, string application, string user, CancellationToken cancellationToken) where T : class, IAuthenticateAsb;
|
||||
|
||||
void InjectCustomBehavior<T>(ChannelFactory<T> channelFactory) where T : class, IAuthenticateAsb;
|
||||
|
||||
bool EstablishSecureSession<T>(ConnectContext<T> context, string solutionName, ClientAccess access) where T : class, IAuthenticateAsb;
|
||||
|
||||
void DisconnectSecureSession<T>(ConnectContext<T> context) where T : class, IAuthenticateAsb;
|
||||
|
||||
IEnumerable<EndpointDiscoveryMetadata> DiscoverEndpointsForContract(XmlQualifiedName contract, string scopeRule);
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
internal interface ISolutionParameters
|
||||
{
|
||||
string DefaultAsbSolutionName { get; }
|
||||
|
||||
string GetSolutionPassphrase(string asbSolution, Action<string> errorMessageHandler);
|
||||
|
||||
byte[] GetSolutionCertificate(string asbSolution);
|
||||
|
||||
string GetSolutionSaltValue(string asbSolution);
|
||||
|
||||
string GetSolutionHashAlgorithm(string asbSolution);
|
||||
|
||||
int GetSolutionPasswordIterations(string asbSolution);
|
||||
|
||||
string GetSolutionInitialVector(string asbSolution);
|
||||
|
||||
int GetSolutionKeySize(string asbSolution);
|
||||
|
||||
BigInteger GetSolutionPrime(string asbSolution);
|
||||
|
||||
BigInteger GetSolutionGenerator(string asbSolution);
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "KeepAlive", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
[XmlRoot(Namespace = "urn:msg.asb.se:2")]
|
||||
public class KeepAliveRequest : ConnectedRequest
|
||||
{
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 0)]
|
||||
[XmlElement(ElementName = "ConsumerAuthenticationData")]
|
||||
public AuthenticationData ConsumerAuthenticationData { get; set; }
|
||||
|
||||
public KeepAliveRequest()
|
||||
{
|
||||
}
|
||||
|
||||
public KeepAliveRequest(AuthenticationData consumerAuthenticationData)
|
||||
{
|
||||
ConsumerAuthenticationData = consumerAuthenticationData;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "KeepAliveResponse", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
[XmlRoot(Namespace = "urn:msg.asb.se:2")]
|
||||
public class KeepAliveResponse : ConnectedResponse
|
||||
{
|
||||
public KeepAliveResponse()
|
||||
{
|
||||
}
|
||||
|
||||
public KeepAliveResponse(ArchestrAResult result, ConnectionState downstreamConnectionState)
|
||||
: base(result, downstreamConnectionState)
|
||||
{
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[Serializable]
|
||||
[XmlType(Namespace = "urn:data.asb.se:2")]
|
||||
public class NamedValue
|
||||
{
|
||||
[XmlElement(ElementName = "Name", Order = 0)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "Value", Order = 1)]
|
||||
public Value Value { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
internal static class NativeMethods
|
||||
{
|
||||
public enum WTS_INFO_CLASS
|
||||
{
|
||||
WTSSessionId = 4,
|
||||
WTSClientName = 10
|
||||
}
|
||||
|
||||
public static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
|
||||
|
||||
public const int WTS_CURRENT_SESSION = -1;
|
||||
|
||||
[DllImport("Wtsapi32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out IntPtr ppBuffer, out int pBytesReturned);
|
||||
|
||||
[DllImport("wtsapi32.dll", ExactSpelling = true)]
|
||||
public static extern void WTSFreeMemory(IntPtr memory);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[Serializable]
|
||||
[XmlType(Namespace = "urn:data.asb.se:2")]
|
||||
public class PublicKey
|
||||
{
|
||||
[XmlElement(ElementName = "Data", DataType = "base64Binary", Order = 0)]
|
||||
public byte[] Data { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "Renew", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
[XmlRoot(Namespace = "urn:msg.asb.se:2")]
|
||||
public class RenewRequest : ConnectedRequest
|
||||
{
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 0)]
|
||||
[XmlElement(ElementName = "ConsumerAuthenticationData")]
|
||||
public AuthenticationData ConsumerAuthenticationData { get; set; }
|
||||
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 1)]
|
||||
[XmlElement(ElementName = "NewConnectionId")]
|
||||
public Guid NewConnectionId { get; set; }
|
||||
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 2)]
|
||||
[XmlElement(ElementName = "NewConsumerPublicKey")]
|
||||
public PublicKey NewConsumerPublicKey { get; set; }
|
||||
|
||||
public RenewRequest()
|
||||
{
|
||||
}
|
||||
|
||||
public RenewRequest(AuthenticationData consumerAuthenticationData, Guid newConnectionId, PublicKey newConsumerPublicKey)
|
||||
{
|
||||
ConsumerAuthenticationData = consumerAuthenticationData;
|
||||
NewConnectionId = newConnectionId;
|
||||
NewConsumerPublicKey = newConsumerPublicKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "RenewResponse", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
[XmlRoot(Namespace = "urn:msg.asb.se:2")]
|
||||
public class RenewResponse : ConnectedResponse
|
||||
{
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 2)]
|
||||
[XmlElement(ElementName = "NewConnectionId")]
|
||||
public Guid NewConnectionId { get; set; }
|
||||
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 3)]
|
||||
[XmlElement(ElementName = "NewConnectionLifetime", DataType = "duration")]
|
||||
public string NewConnectionLifetime { get; set; }
|
||||
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 4)]
|
||||
[XmlElement(ElementName = "NewServicePublicKey")]
|
||||
public PublicKey NewServicePublicKey { get; set; }
|
||||
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 5)]
|
||||
[XmlElement(ElementName = "NewServiceAuthenticationData")]
|
||||
public AuthenticationData NewServiceAuthenticationData { get; set; }
|
||||
|
||||
public RenewResponse()
|
||||
{
|
||||
}
|
||||
|
||||
public RenewResponse(ArchestrAResult result, Guid newConnectionId, string newConnectionLifetime, PublicKey newServicePublicKey, AuthenticationData newServiceAuthenticationData)
|
||||
: base(result)
|
||||
{
|
||||
NewConnectionId = newConnectionId;
|
||||
NewConnectionLifetime = newConnectionLifetime;
|
||||
NewServicePublicKey = newServicePublicKey;
|
||||
NewServiceAuthenticationData = newServiceAuthenticationData;
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public static class SerializationExtensions
|
||||
{
|
||||
private static readonly Dictionary<Type, XmlSerializer> xmlSerializers = new Dictionary<Type, XmlSerializer>();
|
||||
|
||||
private static readonly object serializersLock = new object();
|
||||
|
||||
public static string ToXml(this object value)
|
||||
{
|
||||
string xmlText = string.Empty;
|
||||
if (value != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
using TextWriter textWriter = new StringWriter(CultureInfo.CurrentCulture);
|
||||
lock (serializersLock)
|
||||
{
|
||||
XmlSerializer typeSerializer = GetTypeSerializer(value.GetType());
|
||||
if (typeSerializer != null)
|
||||
{
|
||||
typeSerializer.Serialize(textWriter, value);
|
||||
xmlText = textWriter.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
return NormalizeXmlText(xmlText);
|
||||
}
|
||||
|
||||
private static XmlSerializer GetTypeSerializer(Type objectType)
|
||||
{
|
||||
XmlSerializer xmlSerializer = null;
|
||||
if (objectType != null)
|
||||
{
|
||||
lock (serializersLock)
|
||||
{
|
||||
if (!xmlSerializers.ContainsKey(objectType))
|
||||
{
|
||||
string defaultNamespace = "urn:invensys.schemas";
|
||||
object[] customAttributes = objectType.GetCustomAttributes(inherit: true);
|
||||
for (int i = 0; i < customAttributes.Length; i++)
|
||||
{
|
||||
if (customAttributes[i] is XmlRootAttribute xmlRootAttribute)
|
||||
{
|
||||
defaultNamespace = xmlRootAttribute.Namespace;
|
||||
break;
|
||||
}
|
||||
}
|
||||
xmlSerializer = new XmlSerializer(objectType, defaultNamespace);
|
||||
xmlSerializers.Add(objectType, xmlSerializer);
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlSerializer = xmlSerializers[objectType];
|
||||
}
|
||||
}
|
||||
}
|
||||
return xmlSerializer;
|
||||
}
|
||||
|
||||
private static string NormalizeXmlText(string xmlText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(xmlText))
|
||||
{
|
||||
return xmlText;
|
||||
}
|
||||
if (Environment.Is64BitProcess)
|
||||
{
|
||||
using StringReader textReader = new StringReader(xmlText);
|
||||
using IEnumerator<XElement> enumerator = XDocument.Load(textReader).Elements().GetEnumerator();
|
||||
if (enumerator.MoveNext())
|
||||
{
|
||||
XElement current = enumerator.Current;
|
||||
XAttribute xAttribute = current.Attribute(XNamespace.Xmlns + "xsd");
|
||||
XAttribute xAttribute2 = current.Attribute(XNamespace.Xmlns + "xsi");
|
||||
if (xAttribute != null && xAttribute2 != null)
|
||||
{
|
||||
current.ReplaceAttributes(xAttribute2, xAttribute);
|
||||
}
|
||||
using TextWriter textWriter = new StringWriter(CultureInfo.CurrentCulture);
|
||||
current.Save(textWriter);
|
||||
xmlText = textWriter.ToString();
|
||||
}
|
||||
}
|
||||
return xmlText;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using System.ServiceModel;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract]
|
||||
public class ServiceMessage
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[Serializable]
|
||||
[XmlType(Namespace = "urn:data.asb.se:2")]
|
||||
public class SolutionHandle : SourceHandle
|
||||
{
|
||||
[XmlElement(ElementName = "SolutionName", Order = 0)]
|
||||
public string SolutionName { get; set; }
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using ArchestrAServices.Common;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public class SolutionParameters : ISolutionParameters
|
||||
{
|
||||
public string DefaultAsbSolutionName => SvcUtilities.ReadKeyValue(string.Empty, "DefaultASBSolution");
|
||||
|
||||
public string GetSolutionPassphrase(string asbSolution, Action<string> errorMessageHandler)
|
||||
{
|
||||
string passphrase;
|
||||
string solutionPassphrase = RegistryHandler.GetSolutionPassphrase(asbSolution, out passphrase);
|
||||
if (!string.IsNullOrEmpty(solutionPassphrase))
|
||||
{
|
||||
errorMessageHandler?.Invoke(solutionPassphrase);
|
||||
}
|
||||
return passphrase;
|
||||
}
|
||||
|
||||
public byte[] GetSolutionCertificate(string asbSolution)
|
||||
{
|
||||
string s;
|
||||
if ((s = SvcUtilities.ReadKeyValue(asbSolution, "Certificate")) != null)
|
||||
{
|
||||
return Encoding.UTF8.GetBytes(s);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetSolutionSaltValue(string asbSolution)
|
||||
{
|
||||
string result;
|
||||
if ((result = SvcUtilities.ReadKeyValue(asbSolution, "saltValue")) != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetSolutionHashAlgorithm(string asbSolution)
|
||||
{
|
||||
string result;
|
||||
if ((result = SvcUtilities.ReadKeyValue(asbSolution, "HashAlgorthim")) != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int GetSolutionPasswordIterations(string asbSolution)
|
||||
{
|
||||
string s;
|
||||
if ((s = SvcUtilities.ReadKeyValue(asbSolution, "passowordIterations")) != null && int.TryParse(s, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public string GetSolutionInitialVector(string asbSolution)
|
||||
{
|
||||
string result;
|
||||
if ((result = SvcUtilities.ReadKeyValue(asbSolution, "initailizationVector")) != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int GetSolutionKeySize(string asbSolution)
|
||||
{
|
||||
string s;
|
||||
if ((s = SvcUtilities.ReadKeyValue(asbSolution, "keySize")) != null && int.TryParse(s, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public BigInteger GetSolutionPrime(string asbSolution)
|
||||
{
|
||||
string value;
|
||||
if ((value = SvcUtilities.ReadKeyValue(asbSolution, "Prime")) != null && BigInteger.TryParse(value, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return BigInteger.Zero;
|
||||
}
|
||||
|
||||
public BigInteger GetSolutionGenerator(string asbSolution)
|
||||
{
|
||||
string value;
|
||||
if ((value = SvcUtilities.ReadKeyValue(asbSolution, "Generator")) != null && BigInteger.TryParse(value, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return BigInteger.Zero;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[Serializable]
|
||||
[XmlInclude(typeof(SolutionHandle))]
|
||||
[XmlType(Namespace = "urn:data.asb.se:2")]
|
||||
public class SourceHandle : EntityHandle
|
||||
{
|
||||
[XmlElement(ElementName = "SourceId", Order = 0)]
|
||||
public ushort SourceId { get; set; }
|
||||
}
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
using ArchestrAServices.Common;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public class SysAuthParameters
|
||||
{
|
||||
private const string SDecimal768 = "1552518092300708935130918131258481755631334049434514313202351194902966239949102107258669453876591642442910007680288864229150803718918046342632727613031282983744380820890196288509170691316593175367469551763119843371637221007210577919";
|
||||
|
||||
private static readonly byte[] sOakley768 = new byte[96]
|
||||
{
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 201, 15,
|
||||
218, 162, 33, 104, 194, 52, 196, 198, 98, 139,
|
||||
128, 220, 28, 209, 41, 2, 78, 8, 138, 103,
|
||||
204, 116, 2, 11, 190, 166, 59, 19, 155, 34,
|
||||
81, 74, 8, 121, 142, 52, 4, 221, 239, 149,
|
||||
25, 179, 205, 58, 67, 27, 48, 43, 10, 109,
|
||||
242, 95, 20, 55, 79, 225, 53, 109, 109, 81,
|
||||
194, 69, 228, 133, 181, 118, 98, 94, 126, 198,
|
||||
244, 76, 66, 233, 166, 58, 54, 32, 255, 255,
|
||||
255, 255, 255, 255, 255, 255
|
||||
};
|
||||
|
||||
private const string SDecimal1024 = "179769313486231590770839156793787453197860296048756011706444423684197180216158519368947833795864925541502180565485980503646440548199239100050792877003355816639229553136239076508735759914822574862575007425302077447712589550957937778424442426617334727629299387668709205606050270810842907692932019128194";
|
||||
|
||||
private static readonly byte[] sOakley1024 = new byte[128]
|
||||
{
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 201, 15,
|
||||
218, 162, 33, 104, 194, 52, 196, 198, 98, 139,
|
||||
128, 220, 28, 209, 41, 2, 78, 8, 138, 103,
|
||||
204, 116, 2, 11, 190, 166, 59, 19, 155, 34,
|
||||
81, 74, 8, 121, 142, 52, 4, 221, 239, 149,
|
||||
25, 179, 205, 58, 67, 27, 48, 43, 10, 109,
|
||||
242, 95, 20, 55, 79, 225, 53, 109, 109, 81,
|
||||
194, 69, 228, 133, 181, 118, 98, 94, 126, 198,
|
||||
244, 76, 66, 233, 166, 55, 237, 107, 11, 255,
|
||||
92, 182, 244, 6, 183, 237, 238, 56, 107, 251,
|
||||
90, 137, 159, 165, 174, 159, 36, 17, 124, 75,
|
||||
31, 230, 73, 40, 102, 81, 236, 230, 83, 129,
|
||||
255, 255, 255, 255, 255, 255, 255, 255
|
||||
};
|
||||
|
||||
private const string SDecimal1536 = "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919";
|
||||
|
||||
private static readonly byte[] sOakley1536 = new byte[192]
|
||||
{
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 201, 15,
|
||||
218, 162, 33, 104, 194, 52, 196, 198, 98, 139,
|
||||
128, 220, 28, 209, 41, 2, 78, 8, 138, 103,
|
||||
204, 116, 2, 11, 190, 166, 59, 19, 155, 34,
|
||||
81, 74, 8, 121, 142, 52, 4, 221, 239, 149,
|
||||
25, 179, 205, 58, 67, 27, 48, 43, 10, 109,
|
||||
242, 95, 20, 55, 79, 225, 53, 109, 109, 81,
|
||||
194, 69, 228, 133, 181, 118, 98, 94, 126, 198,
|
||||
244, 76, 66, 233, 166, 55, 237, 107, 11, 255,
|
||||
92, 182, 244, 6, 183, 237, 238, 56, 107, 251,
|
||||
90, 137, 159, 165, 174, 159, 36, 17, 124, 75,
|
||||
31, 230, 73, 40, 102, 81, 236, 228, 91, 61,
|
||||
194, 0, 124, 184, 161, 99, 191, 5, 152, 218,
|
||||
72, 54, 28, 85, 211, 154, 105, 22, 63, 168,
|
||||
253, 36, 207, 95, 131, 101, 93, 35, 220, 163,
|
||||
173, 150, 28, 98, 243, 86, 32, 133, 82, 187,
|
||||
158, 213, 41, 7, 112, 150, 150, 109, 103, 12,
|
||||
53, 78, 74, 188, 152, 4, 241, 116, 108, 8,
|
||||
202, 35, 115, 39, 255, 255, 255, 255, 255, 255,
|
||||
255, 255
|
||||
};
|
||||
|
||||
private const int DhKeySizeDft = 1024;
|
||||
|
||||
private const int DhSecretSizeDft = 160;
|
||||
|
||||
private static readonly string dhPassphraseDft = Environment.MachineName;
|
||||
|
||||
private const uint ConnectionLifetimeDft = 60000u;
|
||||
|
||||
private const string SaltValueDft = "s@1tValue";
|
||||
|
||||
private static readonly string hashAlgorithmDft = CngAlgorithm.MD5.ToString();
|
||||
|
||||
private const int PasswordIterationsDft = 1;
|
||||
|
||||
private const string InitialVectorDft = "ba172e9941be138b";
|
||||
|
||||
private const int KeySizeDft = 256;
|
||||
|
||||
private readonly ISolutionParameters solutionParameterSource;
|
||||
|
||||
private string sysAuthPassphrase;
|
||||
|
||||
public string AsbSolutionName { get; set; }
|
||||
|
||||
public bool AsbSolutionValid { get; set; }
|
||||
|
||||
public int DhKeySize { get; set; }
|
||||
|
||||
public int DhSecretSize { get; set; }
|
||||
|
||||
public BigInteger DhP { get; set; }
|
||||
|
||||
public BigInteger DhG { get; set; }
|
||||
|
||||
public string DhPassphrase
|
||||
{
|
||||
get
|
||||
{
|
||||
return sysAuthPassphrase;
|
||||
}
|
||||
set
|
||||
{
|
||||
ResetToDefaults();
|
||||
sysAuthPassphrase = value;
|
||||
AsbSolutionValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] DhCertificate { get; private set; }
|
||||
|
||||
public uint ConnectionLifetime { get; set; }
|
||||
|
||||
public string SaltValue { get; set; }
|
||||
|
||||
public string HashAlgorithm { get; set; }
|
||||
|
||||
public int PasswordIterations { get; set; }
|
||||
|
||||
public string InitialVector { get; set; }
|
||||
|
||||
public int KeySize { get; set; }
|
||||
|
||||
public SysAuthParameters(string asbSolutionName = null)
|
||||
{
|
||||
ServiceTrace.LogVerbose("SysAuthParameters constructor with asbSolutionName = {0}", string.IsNullOrEmpty(asbSolutionName) ? "<null>" : asbSolutionName);
|
||||
AsbSolutionName = asbSolutionName;
|
||||
solutionParameterSource = new SolutionParameters();
|
||||
AsbSolutionValid = false;
|
||||
ResetToDefaults();
|
||||
LoadAsbSolution();
|
||||
}
|
||||
|
||||
internal SysAuthParameters(string asbSolutionName, ISolutionParameters parameters)
|
||||
{
|
||||
ServiceTrace.LogVerbose("SysAuthParameters constructor with asbSolutionName = {0}", string.IsNullOrEmpty(asbSolutionName) ? "<null>" : asbSolutionName);
|
||||
AsbSolutionName = asbSolutionName;
|
||||
solutionParameterSource = parameters;
|
||||
AsbSolutionValid = false;
|
||||
ResetToDefaults();
|
||||
LoadAsbSolution();
|
||||
}
|
||||
|
||||
private void ResetToDefaults()
|
||||
{
|
||||
AsbSolutionValid = false;
|
||||
DhKeySize = 1024;
|
||||
DhSecretSize = 160;
|
||||
sysAuthPassphrase = dhPassphraseDft;
|
||||
DhCertificate = null;
|
||||
ConnectionLifetime = 60000u;
|
||||
SaltValue = "s@1tValue";
|
||||
HashAlgorithm = hashAlgorithmDft;
|
||||
PasswordIterations = 1;
|
||||
InitialVector = "ba172e9941be138b";
|
||||
KeySize = 256;
|
||||
GenerateKey();
|
||||
}
|
||||
|
||||
private void LoadAsbSolution()
|
||||
{
|
||||
ServiceTrace.LogResume("LoadAsbSolution entry");
|
||||
try
|
||||
{
|
||||
if (solutionParameterSource == null)
|
||||
{
|
||||
ServiceTrace.LogError("LoadAsbSolution: No solution source provided");
|
||||
AsbSolutionValid = false;
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(AsbSolutionName))
|
||||
{
|
||||
AsbSolutionName = solutionParameterSource.DefaultAsbSolutionName;
|
||||
if (string.IsNullOrEmpty(AsbSolutionName))
|
||||
{
|
||||
ServiceTrace.LogError("LoadAsbSolution: Unable to get default ASB solution name");
|
||||
AsbSolutionValid = false;
|
||||
}
|
||||
ServiceTrace.LogInfo("LoadAsbSolution created with empty solution name, resetting to default '{0}'", string.IsNullOrEmpty(AsbSolutionName) ? "<null>" : AsbSolutionName);
|
||||
}
|
||||
AsbSolutionValid = false;
|
||||
if (!string.IsNullOrEmpty(AsbSolutionName))
|
||||
{
|
||||
ServiceTrace.LogVerbose("LoadAsbSolution: Using solution {0}", AsbSolutionName);
|
||||
string errorMessage = string.Empty;
|
||||
DhPassphrase = solutionParameterSource.GetSolutionPassphrase(AsbSolutionName, delegate(string msg)
|
||||
{
|
||||
errorMessage = msg;
|
||||
});
|
||||
if (string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
DhCertificate = solutionParameterSource.GetSolutionCertificate(AsbSolutionName);
|
||||
SaltValue = solutionParameterSource.GetSolutionSaltValue(AsbSolutionName);
|
||||
HashAlgorithm = solutionParameterSource.GetSolutionHashAlgorithm(AsbSolutionName);
|
||||
PasswordIterations = solutionParameterSource.GetSolutionPasswordIterations(AsbSolutionName);
|
||||
InitialVector = solutionParameterSource.GetSolutionInitialVector(AsbSolutionName);
|
||||
KeySize = solutionParameterSource.GetSolutionKeySize(AsbSolutionName);
|
||||
DhP = solutionParameterSource.GetSolutionPrime(AsbSolutionName);
|
||||
DhG = solutionParameterSource.GetSolutionGenerator(AsbSolutionName);
|
||||
AsbSolutionValid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ServiceTrace.LogError("LoadAsbSolution: Error loading passphrase: " + errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServiceTrace.LogError("LoadAsbSolution: Exception caught: {0}", ex.Message);
|
||||
AsbSolutionValid = false;
|
||||
}
|
||||
ServiceTrace.LogSuspend("LoadAsbSolution exit");
|
||||
}
|
||||
|
||||
private void GenerateKey()
|
||||
{
|
||||
bool flag;
|
||||
BigInteger result;
|
||||
if (DhKeySize == 768)
|
||||
{
|
||||
flag = BigInteger.TryParse("1552518092300708935130918131258481755631334049434514313202351194902966239949102107258669453876591642442910007680288864229150803718918046342632727613031282983744380820890196288509170691316593175367469551763119843371637221007210577919", out result);
|
||||
}
|
||||
else if (DhKeySize == 1024)
|
||||
{
|
||||
flag = BigInteger.TryParse("179769313486231590770839156793787453197860296048756011706444423684197180216158519368947833795864925541502180565485980503646440548199239100050792877003355816639229553136239076508735759914822574862575007425302077447712589550957937778424442426617334727629299387668709205606050270810842907692932019128194", out result);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DhKeySize != 1536)
|
||||
{
|
||||
throw new ArgumentException("Invalid bit size.");
|
||||
}
|
||||
flag = BigInteger.TryParse("2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919", out result);
|
||||
}
|
||||
if (flag)
|
||||
{
|
||||
DhP = result;
|
||||
}
|
||||
DhG = new BigInteger(22);
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public class SysAuthenticatorClientCache : SynchronizedKeyedCollection<Guid, SystemAuthenticationClientAuthentication>
|
||||
{
|
||||
private static readonly SysAuthenticatorClientCache clientConnections = new SysAuthenticatorClientCache();
|
||||
|
||||
private static object cacheLockObject = new object();
|
||||
|
||||
private SysAuthenticatorClientCache()
|
||||
{
|
||||
}
|
||||
|
||||
public static IEnumerable<SystemAuthenticationClientAuthentication> GetAllClientAuthenticators()
|
||||
{
|
||||
List<SystemAuthenticationClientAuthentication> list = new List<SystemAuthenticationClientAuthentication>();
|
||||
lock (cacheLockObject)
|
||||
{
|
||||
list.AddRange(clientConnections.Items);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddClientAuthenticator(SystemAuthenticationClientAuthentication clientAuthenticator)
|
||||
{
|
||||
if (clientAuthenticator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lock (cacheLockObject)
|
||||
{
|
||||
if (!clientConnections.Contains(clientAuthenticator.ConnectionId))
|
||||
{
|
||||
clientConnections.Add(clientAuthenticator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static SystemAuthenticationClientAuthentication GetClientAuthenticator(Guid connectionId)
|
||||
{
|
||||
SystemAuthenticationClientAuthentication result = null;
|
||||
lock (cacheLockObject)
|
||||
{
|
||||
if (clientConnections.Contains(connectionId))
|
||||
{
|
||||
result = clientConnections[connectionId];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static SystemAuthenticationClientAuthentication RemoveClientAuthenticator(Guid connectionId)
|
||||
{
|
||||
SystemAuthenticationClientAuthentication result = null;
|
||||
lock (cacheLockObject)
|
||||
{
|
||||
if (clientConnections.Contains(connectionId))
|
||||
{
|
||||
result = clientConnections[connectionId];
|
||||
clientConnections.Remove(connectionId);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override Guid GetKeyForItem(SystemAuthenticationClientAuthentication item)
|
||||
{
|
||||
return item.ConnectionId;
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public class SysAuthenticatorServiceCache : SynchronizedKeyedCollection<Guid, SystemAuthenticationServiceAuthentication>
|
||||
{
|
||||
private static readonly SysAuthenticatorServiceCache serviceConnections = new SysAuthenticatorServiceCache();
|
||||
|
||||
private static readonly object serviceCacheLockObject = new object();
|
||||
|
||||
private SysAuthenticatorServiceCache()
|
||||
{
|
||||
}
|
||||
|
||||
public static IEnumerable<SystemAuthenticationServiceAuthentication> GetAllServiceAuthenticators()
|
||||
{
|
||||
return serviceConnections.Items.AsEnumerable();
|
||||
}
|
||||
|
||||
public static void AddServiceAuthenticator(SystemAuthenticationServiceAuthentication serviceAuthenticator)
|
||||
{
|
||||
if (serviceAuthenticator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lock (serviceCacheLockObject)
|
||||
{
|
||||
if (!serviceConnections.Contains(serviceAuthenticator.ConnectionId))
|
||||
{
|
||||
serviceConnections.Add(serviceAuthenticator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static SystemAuthenticationServiceAuthentication GetServiceAuthenticator(Guid connectionId)
|
||||
{
|
||||
SystemAuthenticationServiceAuthentication result = null;
|
||||
lock (serviceCacheLockObject)
|
||||
{
|
||||
if (serviceConnections.Contains(connectionId))
|
||||
{
|
||||
result = serviceConnections[connectionId];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static SystemAuthenticationServiceAuthentication RemoveServiceAuthenticator(Guid connectionId)
|
||||
{
|
||||
SystemAuthenticationServiceAuthentication serviceAuthenticator = GetServiceAuthenticator(connectionId);
|
||||
if (serviceAuthenticator != null)
|
||||
{
|
||||
lock (serviceCacheLockObject)
|
||||
{
|
||||
serviceConnections.Remove(connectionId);
|
||||
}
|
||||
}
|
||||
return serviceAuthenticator;
|
||||
}
|
||||
|
||||
protected override Guid GetKeyForItem(SystemAuthenticationServiceAuthentication item)
|
||||
{
|
||||
return item.ConnectionId;
|
||||
}
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using ArchestrAServices.Common;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public class SystemAuthenticationClientAuthentication : SystemAuthenticationConnectionBase
|
||||
{
|
||||
public uint Timeout { get; set; }
|
||||
|
||||
public SystemAuthenticationClientAuthentication(string asbSolution)
|
||||
: base(asbSolution)
|
||||
{
|
||||
Reset();
|
||||
base.ReasonSecureSessionNotEstablished = "Constructed";
|
||||
}
|
||||
|
||||
public static bool EstablishSecureSession(string asbSolution, ClientMetadata clientMetadata, Func<ConnectRequest, ConnectResponse> connectDelegate, Func<AuthenticateMeRequest, AuthenticateMeResponse> authenticateMeDelegate, Action<Guid> connectionIdHandler, Action<string> reasonSecureSessionNotEstablishedHandler)
|
||||
{
|
||||
SystemAuthenticationClientAuthentication systemAuthenticationClientAuthentication = new SystemAuthenticationClientAuthentication(asbSolution);
|
||||
SysAuthenticatorClientCache.AddClientAuthenticator(systemAuthenticationClientAuthentication);
|
||||
Guid connectionId = systemAuthenticationClientAuthentication.ConnectionId;
|
||||
PublicKey consumerPublicKey = new PublicKey
|
||||
{
|
||||
Data = systemAuthenticationClientAuthentication.LocalPublicKey
|
||||
};
|
||||
ConnectRequest arg = new ConnectRequest(connectionId, consumerPublicKey, asbSolution);
|
||||
ConnectResponse connectResponse = null;
|
||||
try
|
||||
{
|
||||
connectResponse = connectDelegate?.Invoke(arg);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string text = string.Format(CultureInfo.CurrentCulture, "Exception connecting to service during EstablishSecureSession: {0}", new object[1] { ex.Message });
|
||||
ServiceTrace.LogWarning(text);
|
||||
reasonSecureSessionNotEstablishedHandler?.Invoke(text);
|
||||
}
|
||||
if (connectResponse != null)
|
||||
{
|
||||
if (connectResponse.Result.Success)
|
||||
{
|
||||
systemAuthenticationClientAuthentication.RemotePublicKey = connectResponse.ServicePublicKey.Data;
|
||||
systemAuthenticationClientAuthentication.ReasonSecureSessionNotEstablished = string.Empty;
|
||||
if (systemAuthenticationClientAuthentication.ValidResponse(connectResponse, forceHmac: true))
|
||||
{
|
||||
byte[] initializationVector;
|
||||
AuthenticateMeRequest authenticateMeRequest = new AuthenticateMeRequest(new AuthenticationData
|
||||
{
|
||||
Data = systemAuthenticationClientAuthentication.CalculateAuthenticationData(systemAuthenticationClientAuthentication.LocalPublicKey, systemAuthenticationClientAuthentication.RemotePublicKey, out initializationVector),
|
||||
InitializationVector = initializationVector
|
||||
}, clientMetadata);
|
||||
systemAuthenticationClientAuthentication.Sign(authenticateMeRequest, forceHmac: true);
|
||||
AuthenticateMeResponse authenticateMeResponse = authenticateMeDelegate(authenticateMeRequest);
|
||||
if (authenticateMeResponse != null && authenticateMeResponse.Result.Success)
|
||||
{
|
||||
systemAuthenticationClientAuthentication.SecureSessionEstablished = true;
|
||||
connectionIdHandler?.Invoke(connectionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
ServiceTrace.LogVerbose("Service rejected the connection at the AuthenticateMe stage");
|
||||
string obj = "Service rejected the connection at the AuthenticateMe stage";
|
||||
if (authenticateMeResponse != null)
|
||||
{
|
||||
obj = ((authenticateMeResponse.Result.ErrorMessages == null || authenticateMeResponse.Result.ErrorMessages.Length == 0) ? string.Format(CultureInfo.CurrentCulture, "Service returned error {0} in response to AuthenticateMe method, cannot establish secure connection.", new object[1] { authenticateMeResponse.Result.ResultCodeAsError }) : authenticateMeResponse.Result.ErrorMessages[0]);
|
||||
}
|
||||
reasonSecureSessionNotEstablishedHandler?.Invoke(obj);
|
||||
SysAuthenticatorClientCache.RemoveClientAuthenticator(connectionId);
|
||||
connectionIdHandler?.Invoke(Guid.Empty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(systemAuthenticationClientAuthentication.ReasonSecureSessionNotEstablished))
|
||||
{
|
||||
reasonSecureSessionNotEstablishedHandler?.Invoke("Service returned response to Connect method, but validation failed: " + systemAuthenticationClientAuthentication.ReasonSecureSessionNotEstablished);
|
||||
}
|
||||
else
|
||||
{
|
||||
reasonSecureSessionNotEstablishedHandler?.Invoke("Service returned response to Connect method, but validation data was not valid, cannot establish secure session");
|
||||
}
|
||||
SysAuthenticatorClientCache.RemoveClientAuthenticator(connectionId);
|
||||
connectionIdHandler?.Invoke(Guid.Empty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (connectResponse.Result.ErrorMessages != null && connectResponse.Result.ErrorMessages.Length != 0)
|
||||
{
|
||||
reasonSecureSessionNotEstablishedHandler?.Invoke(string.Format(CultureInfo.CurrentCulture, "Service returned error {0} in response to Connect method, cannot establish secure connection. Additional information:", new object[1] { connectResponse.Result.ResultCodeAsError }));
|
||||
string[] errorMessages = connectResponse.Result.ErrorMessages;
|
||||
foreach (string obj2 in errorMessages)
|
||||
{
|
||||
reasonSecureSessionNotEstablishedHandler?.Invoke(obj2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reasonSecureSessionNotEstablishedHandler?.Invoke(string.Format(CultureInfo.CurrentCulture, "Service returned error {0} in response to Connect method, cannot establish secure connection.", new object[1] { connectResponse.Result.ResultCodeAsError }));
|
||||
}
|
||||
SysAuthenticatorClientCache.RemoveClientAuthenticator(connectionId);
|
||||
connectionIdHandler?.Invoke(Guid.Empty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SysAuthenticatorClientCache.RemoveClientAuthenticator(connectionId);
|
||||
connectionIdHandler?.Invoke(Guid.Empty);
|
||||
}
|
||||
return systemAuthenticationClientAuthentication.SecureSessionEstablished;
|
||||
}
|
||||
|
||||
public void AbortSession()
|
||||
{
|
||||
Reset();
|
||||
base.ReasonSecureSessionNotEstablished = "Session Aborted";
|
||||
}
|
||||
|
||||
public static void DisconnectSecureSession(Guid connectionId, Action<DisconnectRequest> disconnectDelegate)
|
||||
{
|
||||
SystemAuthenticationClientAuthentication clientAuthenticator = SysAuthenticatorClientCache.GetClientAuthenticator(connectionId);
|
||||
if (clientAuthenticator != null)
|
||||
{
|
||||
byte[] initializationVector;
|
||||
DisconnectRequest disconnectRequest = new DisconnectRequest(new AuthenticationData
|
||||
{
|
||||
Data = clientAuthenticator.EncypherWithNewInitializationVector(Encoding.UTF8.GetBytes(clientAuthenticator.ConnectionId.ToString()), out initializationVector),
|
||||
InitializationVector = initializationVector
|
||||
});
|
||||
clientAuthenticator.Sign(disconnectRequest, forceHmac: false);
|
||||
disconnectDelegate?.Invoke(disconnectRequest);
|
||||
}
|
||||
}
|
||||
|
||||
private new void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
Timeout = 10000u;
|
||||
}
|
||||
}
|
||||
+509
@@ -0,0 +1,509 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using ArchestrAServices.Common;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public class SystemAuthenticationConnectionBase
|
||||
{
|
||||
private static readonly RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
|
||||
|
||||
private static readonly byte[] passwordsalt = Encoding.ASCII.GetBytes("ArchestrAService");
|
||||
|
||||
protected static string AsbAuthenticationVersion = "V2";
|
||||
|
||||
protected static readonly object MessageNumberLock = new object();
|
||||
|
||||
private readonly List<ulong> outOfSyncMessageNumbers = new List<ulong>();
|
||||
|
||||
private ulong highestMessageNumberReceived;
|
||||
|
||||
private byte[] cryptoKey;
|
||||
|
||||
private byte[] encryptionKey;
|
||||
|
||||
protected SysAuthParameters SolutionParameters;
|
||||
|
||||
protected byte[] PrivateKey;
|
||||
|
||||
protected byte[] LocalPublicKey;
|
||||
|
||||
protected byte[] RemotePublicKey;
|
||||
|
||||
protected ulong NextMessageNumber = 1uL;
|
||||
|
||||
internal static ISolutionParameters TestParameters { private get; set; }
|
||||
|
||||
internal byte[] CryptoKey => cryptoKey ?? (cryptoKey = CalculateCryptoKey());
|
||||
|
||||
internal byte[] EncryptionKey => encryptionKey ?? (encryptionKey = CalculateEncryptionKey());
|
||||
|
||||
private byte[] SolutionPassphrase
|
||||
{
|
||||
get
|
||||
{
|
||||
byte[] result = null;
|
||||
if (SolutionParameters.AsbSolutionValid)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SolutionParameters.DhPassphrase))
|
||||
{
|
||||
result = Encoding.UTF8.GetBytes(SolutionParameters.DhPassphrase);
|
||||
}
|
||||
if (SolutionParameters.DhCertificate != null && SolutionParameters.DhCertificate.Length != 0)
|
||||
{
|
||||
result = new X509Certificate(SolutionParameters.DhCertificate).GetPublicKey();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public Guid ConnectionId { get; set; } = Guid.Empty;
|
||||
|
||||
public ConnectionValidator ConnectionValidator { get; protected set; }
|
||||
|
||||
public bool SecureSessionEstablished { get; protected set; }
|
||||
|
||||
public string ReasonSecureSessionNotEstablished { get; protected set; }
|
||||
|
||||
public string DhPassphrase
|
||||
{
|
||||
get
|
||||
{
|
||||
return SolutionParameters.DhPassphrase;
|
||||
}
|
||||
set
|
||||
{
|
||||
SolutionParameters.DhPassphrase = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string DhHashAlgorithm => SolutionParameters.HashAlgorithm;
|
||||
|
||||
public string DhAsbSolutionName => SolutionParameters.AsbSolutionName;
|
||||
|
||||
public SystemAuthenticationConnectionBase(string asbSolutionName = null)
|
||||
{
|
||||
if (TestParameters != null)
|
||||
{
|
||||
SolutionParameters = new SysAuthParameters(asbSolutionName, TestParameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
SolutionParameters = new SysAuthParameters(asbSolutionName);
|
||||
}
|
||||
ReasonSecureSessionNotEstablished = "Constructed";
|
||||
Reset();
|
||||
}
|
||||
|
||||
protected void Reset()
|
||||
{
|
||||
SecureSessionEstablished = false;
|
||||
ReasonSecureSessionNotEstablished = "Reset";
|
||||
ConnectionId = Guid.NewGuid();
|
||||
PrivateKey = GetPrivateKey(SolutionParameters.KeySize);
|
||||
LocalPublicKey = CalculatePublicKey(PrivateKey);
|
||||
cryptoKey = null;
|
||||
SecureSessionEstablished = false;
|
||||
}
|
||||
|
||||
private byte[] GetPrivateKey(int length)
|
||||
{
|
||||
byte[] array = null;
|
||||
if (length > 0)
|
||||
{
|
||||
BigInteger bigInteger = SolutionParameters.DhP - new BigInteger(1);
|
||||
BigInteger bigInteger2 = new BigInteger(0);
|
||||
while (bigInteger2 >= bigInteger || bigInteger2 <= 0L)
|
||||
{
|
||||
array = new byte[length / 8];
|
||||
random.GetBytes(array);
|
||||
bigInteger2 = new BigInteger(array);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
protected byte[] CalculatePublicKey(byte[] privateKey)
|
||||
{
|
||||
BigInteger exponent = new BigInteger(privateKey);
|
||||
BigInteger dhG = SolutionParameters.DhG;
|
||||
BigInteger dhP = SolutionParameters.DhP;
|
||||
return BigInteger.ModPow(dhG, exponent, dhP).ToByteArray();
|
||||
}
|
||||
|
||||
protected byte[] CalculateConnectionKey(byte[] remotePublicKey, byte[] localPrivateKey)
|
||||
{
|
||||
BigInteger value = new BigInteger(remotePublicKey);
|
||||
BigInteger exponent = new BigInteger(localPrivateKey);
|
||||
BigInteger dhP = SolutionParameters.DhP;
|
||||
return BigInteger.ModPow(value, exponent, dhP).ToByteArray();
|
||||
}
|
||||
|
||||
private HMAC NewSolutionHmac(bool forceHmac)
|
||||
{
|
||||
HMAC result;
|
||||
switch (DhHashAlgorithm.ToLower())
|
||||
{
|
||||
case "md5":
|
||||
ServiceTrace.LogVerbose("Solution HMAC is MD5");
|
||||
result = new HMACMD5(CryptoKey);
|
||||
break;
|
||||
case "sha1":
|
||||
ServiceTrace.LogVerbose("Solution HMAC is SHA1");
|
||||
result = new HMACSHA1(CryptoKey);
|
||||
break;
|
||||
case "sha512":
|
||||
ServiceTrace.LogVerbose("Solution HMAC is SHA512");
|
||||
result = new HMACSHA512(CryptoKey);
|
||||
break;
|
||||
default:
|
||||
ServiceTrace.LogVerbose("Solution HMAC is NONE");
|
||||
result = null;
|
||||
if (forceHmac)
|
||||
{
|
||||
result = new HMACSHA1(CryptoKey);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool ValidRequest(ConnectedRequest request, bool forceHmac)
|
||||
{
|
||||
bool flag = false;
|
||||
if (request == null)
|
||||
{
|
||||
ReasonSecureSessionNotEstablished = "ValidRequest: The message is null, cannot validate";
|
||||
return false;
|
||||
}
|
||||
if (request.ConnectionValidator == null)
|
||||
{
|
||||
ReasonSecureSessionNotEstablished = "ValidRequest: The message ConnectionValidator field is null, cannot validate";
|
||||
return false;
|
||||
}
|
||||
ConnectionValidator connectionValidator = request.ConnectionValidator;
|
||||
byte[] messageAuthenticationCode = connectionValidator.MessageAuthenticationCode;
|
||||
byte[] signatureInitializationVector = connectionValidator.SignatureInitializationVector;
|
||||
byte[] array = null;
|
||||
using (HMAC hMAC = NewSolutionHmac(forceHmac))
|
||||
{
|
||||
if (hMAC != null)
|
||||
{
|
||||
connectionValidator.MessageAuthenticationCode = new byte[0];
|
||||
connectionValidator.SignatureInitializationVector = new byte[0];
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(request.ToXml());
|
||||
connectionValidator.MessageAuthenticationCode = messageAuthenticationCode;
|
||||
connectionValidator.SignatureInitializationVector = signatureInitializationVector;
|
||||
byte[] array2 = hMAC.ComputeHash(bytes);
|
||||
byte[] array3 = Decypher(messageAuthenticationCode, signatureInitializationVector);
|
||||
bool flag2 = false;
|
||||
if (array3 != null && array2.Length == array3.Length)
|
||||
{
|
||||
flag2 = true;
|
||||
for (int i = 0; i < array2.Length; i++)
|
||||
{
|
||||
if (array2[i] != array3[i])
|
||||
{
|
||||
flag2 = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
array = ((!flag2) ? new byte[0] : messageAuthenticationCode);
|
||||
}
|
||||
}
|
||||
if (array != null)
|
||||
{
|
||||
if (messageAuthenticationCode != null && array.Length == messageAuthenticationCode.Length)
|
||||
{
|
||||
flag = true;
|
||||
for (int j = 0; j < messageAuthenticationCode.Length; j++)
|
||||
{
|
||||
if (messageAuthenticationCode[j] != array[j])
|
||||
{
|
||||
if (string.IsNullOrEmpty(ReasonSecureSessionNotEstablished))
|
||||
{
|
||||
ReasonSecureSessionNotEstablished = string.Format(CultureInfo.CurrentCulture, "ValidRequest: Received message hmac correct length {0} but differs at byte {1}, cannot validate", new object[2] { messageAuthenticationCode.Length, j });
|
||||
}
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (messageAuthenticationCode == null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ReasonSecureSessionNotEstablished))
|
||||
{
|
||||
ReasonSecureSessionNotEstablished = "ValidRequest: Received message has null hmac, cannot validate";
|
||||
}
|
||||
}
|
||||
else if (string.IsNullOrEmpty(ReasonSecureSessionNotEstablished))
|
||||
{
|
||||
ReasonSecureSessionNotEstablished = string.Format(CultureInfo.CurrentCulture, "ValidRequest: Received message hmac wrong length, cannot validate (received {0}, computed {1})", new object[2] { messageAuthenticationCode.Length, array.Length });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
flag = true;
|
||||
}
|
||||
if (flag)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (connectionValidator.MessageNumber <= highestMessageNumberReceived)
|
||||
{
|
||||
ReasonSecureSessionNotEstablished = string.Format(CultureInfo.CurrentCulture, "ValidRequest: Received message out of sequence, cannot validate (current {0}, highest {1})", new object[2] { connectionValidator.MessageNumber, highestMessageNumberReceived });
|
||||
flag = false;
|
||||
}
|
||||
else if (outOfSyncMessageNumbers.Contains(connectionValidator.MessageNumber))
|
||||
{
|
||||
ReasonSecureSessionNotEstablished = string.Format(CultureInfo.CurrentCulture, "ValidRequest: Received message received late, cannot validate (current {0})", new object[1] { connectionValidator.MessageNumber });
|
||||
flag = false;
|
||||
}
|
||||
else if (connectionValidator.MessageNumber == highestMessageNumberReceived + 1)
|
||||
{
|
||||
highestMessageNumberReceived = connectionValidator.MessageNumber;
|
||||
}
|
||||
else
|
||||
{
|
||||
outOfSyncMessageNumbers.Add(connectionValidator.MessageNumber);
|
||||
}
|
||||
outOfSyncMessageNumbers.Sort();
|
||||
foreach (ulong outOfSyncMessageNumber in outOfSyncMessageNumbers)
|
||||
{
|
||||
if (outOfSyncMessageNumber == highestMessageNumberReceived + 1)
|
||||
{
|
||||
highestMessageNumberReceived = outOfSyncMessageNumber;
|
||||
}
|
||||
}
|
||||
List<ulong> list = new List<ulong>();
|
||||
foreach (ulong outOfSyncMessageNumber2 in outOfSyncMessageNumbers)
|
||||
{
|
||||
if (outOfSyncMessageNumber2 <= highestMessageNumberReceived)
|
||||
{
|
||||
list.Add(outOfSyncMessageNumber2);
|
||||
}
|
||||
}
|
||||
foreach (ulong item in list)
|
||||
{
|
||||
outOfSyncMessageNumbers.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
public bool ValidResponse(ConnectedResponse response, bool forceHmac = false)
|
||||
{
|
||||
if (response != null && response.Result.Success)
|
||||
{
|
||||
return ValidRequest(response, forceHmac);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected byte[] ReCalculateAuthenticationData(byte[] leftPart, byte[] rightPart, byte[] initializationVector)
|
||||
{
|
||||
List<byte> list = new List<byte>();
|
||||
if (leftPart != null)
|
||||
{
|
||||
list.AddRange(leftPart);
|
||||
}
|
||||
if (rightPart != null)
|
||||
{
|
||||
list.AddRange(rightPart);
|
||||
}
|
||||
return ReEncypher(list.ToArray(), initializationVector);
|
||||
}
|
||||
|
||||
protected byte[] CalculateAuthenticationData(byte[] leftPart, byte[] rightPart, out byte[] initializationVector)
|
||||
{
|
||||
List<byte> list = new List<byte>();
|
||||
if (leftPart != null)
|
||||
{
|
||||
list.AddRange(leftPart);
|
||||
}
|
||||
if (rightPart != null)
|
||||
{
|
||||
list.AddRange(rightPart);
|
||||
}
|
||||
return EncypherWithNewInitializationVector(list.ToArray(), out initializationVector);
|
||||
}
|
||||
|
||||
public void Sign(ConnectedRequest request, bool forceHmac)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lock (MessageNumberLock)
|
||||
{
|
||||
request.ConnectionValidator = new ConnectionValidator
|
||||
{
|
||||
ConnectionId = ConnectionId,
|
||||
MessageNumber = NextMessageNumber++,
|
||||
MessageAuthenticationCode = new byte[0],
|
||||
SignatureInitializationVector = new byte[0]
|
||||
};
|
||||
using HMAC hMAC = NewSolutionHmac(forceHmac);
|
||||
if (hMAC != null)
|
||||
{
|
||||
string text = request.ToXml();
|
||||
ServiceTrace.LogVerbose("Signing XML message is {0}", text);
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(text);
|
||||
request.ConnectionValidator.MessageAuthenticationCode = EncypherWithNewInitializationVector(hMAC.ComputeHash(bytes), out var initializationVector);
|
||||
request.ConnectionValidator.SignatureInitializationVector = initializationVector;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Encypher(byte[] clearData, byte[] initializationVector)
|
||||
{
|
||||
byte[] result = null;
|
||||
if (clearData != null)
|
||||
{
|
||||
using AesManaged aesManaged = new AesManaged();
|
||||
using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(CryptoKey.ToBase64(), passwordsalt);
|
||||
aesManaged.Key = rfc2898DeriveBytes.GetBytes(16);
|
||||
aesManaged.IV = initializationVector;
|
||||
using ICryptoTransform transform = aesManaged.CreateEncryptor();
|
||||
using MemoryStream memoryStream = new MemoryStream();
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(clearData, 0, clearData.Length);
|
||||
cryptoStream.Close();
|
||||
}
|
||||
result = memoryStream.ToArray();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte[] EncypherWithNewInitializationVector(byte[] clearData, out byte[] initializationVector)
|
||||
{
|
||||
byte[] result = null;
|
||||
initializationVector = null;
|
||||
if (clearData != null)
|
||||
{
|
||||
using AesManaged aesManaged = new AesManaged();
|
||||
using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(CryptoKey.ToBase64(), passwordsalt);
|
||||
aesManaged.Key = rfc2898DeriveBytes.GetBytes(16);
|
||||
initializationVector = aesManaged.IV;
|
||||
using ICryptoTransform transform = aesManaged.CreateEncryptor();
|
||||
using MemoryStream memoryStream = new MemoryStream();
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(clearData, 0, clearData.Length);
|
||||
cryptoStream.Close();
|
||||
}
|
||||
result = memoryStream.ToArray();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public string Encypher(string clearText, out byte[] initializationVector)
|
||||
{
|
||||
string result = string.Empty;
|
||||
initializationVector = null;
|
||||
if (!string.IsNullOrEmpty(clearText))
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(clearText);
|
||||
result = EncypherWithNewInitializationVector(bytes, out initializationVector).ToBase64();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte[] ReEncypher(byte[] clearData, byte[] initializationVector)
|
||||
{
|
||||
byte[] result = null;
|
||||
if (clearData != null)
|
||||
{
|
||||
using AesManaged aesManaged = new AesManaged();
|
||||
using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(CryptoKey.ToBase64(), passwordsalt);
|
||||
aesManaged.Key = rfc2898DeriveBytes.GetBytes(16);
|
||||
aesManaged.IV = initializationVector;
|
||||
using ICryptoTransform transform = aesManaged.CreateEncryptor();
|
||||
using MemoryStream memoryStream = new MemoryStream();
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(clearData, 0, clearData.Length);
|
||||
cryptoStream.Close();
|
||||
}
|
||||
result = memoryStream.ToArray();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public string ReEncypher(string clearText, byte[] initializationVector)
|
||||
{
|
||||
string result = string.Empty;
|
||||
if (!string.IsNullOrEmpty(clearText))
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(clearText);
|
||||
result = ReEncypher(bytes, initializationVector).ToBase64();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte[] Decypher(byte[] cypherData, byte[] initializationVector)
|
||||
{
|
||||
byte[] result = null;
|
||||
if (cypherData != null)
|
||||
{
|
||||
using AesManaged aesManaged = new AesManaged();
|
||||
using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(CryptoKey.ToBase64(), passwordsalt);
|
||||
aesManaged.Key = rfc2898DeriveBytes.GetBytes(16);
|
||||
aesManaged.IV = initializationVector;
|
||||
try
|
||||
{
|
||||
using ICryptoTransform transform = aesManaged.CreateDecryptor();
|
||||
using MemoryStream stream = new MemoryStream(cypherData);
|
||||
using CryptoStream cryptoStream = new CryptoStream(stream, transform, CryptoStreamMode.Read);
|
||||
using MemoryStream memoryStream = new MemoryStream();
|
||||
cryptoStream.CopyTo(memoryStream);
|
||||
result = memoryStream.ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ReasonSecureSessionNotEstablished = "Decypher failed: " + ex.Message;
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public string Decypher(string cypherText, byte[] initializationVector)
|
||||
{
|
||||
string result = string.Empty;
|
||||
if (!string.IsNullOrEmpty(cypherText))
|
||||
{
|
||||
byte[] cypherData = cypherText.FromBase64ToByteArray();
|
||||
byte[] bytes = Decypher(cypherData, initializationVector);
|
||||
result = Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte[] CalculateCryptoKey()
|
||||
{
|
||||
List<byte> list = new List<byte>();
|
||||
list.AddRange(CalculateConnectionKey(RemotePublicKey, PrivateKey));
|
||||
byte[] solutionPassphrase = SolutionPassphrase;
|
||||
if (solutionPassphrase != null)
|
||||
{
|
||||
list.AddRange(solutionPassphrase);
|
||||
}
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
private byte[] CalculateEncryptionKey()
|
||||
{
|
||||
return new HMACMD5(CryptoKey).ComputeHash(CryptoKey);
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using ArchestrAServices.Common;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public class SystemAuthenticationServiceAuthentication : SystemAuthenticationConnectionBase
|
||||
{
|
||||
public ulong Lifetime { get; private set; }
|
||||
|
||||
public SystemAuthenticationServiceAuthentication()
|
||||
{
|
||||
Reset();
|
||||
Lifetime = 18000000uL;
|
||||
}
|
||||
|
||||
public static ConnectResponse ProcessClientConnection(ConnectRequest request)
|
||||
{
|
||||
ServiceTrace.LogResume("SystemAuthenticationServiceAuthentication.ProcessClientConnection entry");
|
||||
if (request == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ServiceTrace.LogVerbose("SysSvcAuth: Processing client Connect() call '{0}'", request.ConnectionId);
|
||||
if (request.ConsumerPublicKey == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (request.ConsumerPublicKey.Data == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
SystemAuthenticationServiceAuthentication systemAuthenticationServiceAuthentication = new SystemAuthenticationServiceAuthentication
|
||||
{
|
||||
ConnectionId = request.ConnectionId,
|
||||
RemotePublicKey = request.ConsumerPublicKey.Data
|
||||
};
|
||||
SysAuthenticatorServiceCache.AddServiceAuthenticator(systemAuthenticationServiceAuthentication);
|
||||
PublicKey servicePublicKey = new PublicKey
|
||||
{
|
||||
Data = systemAuthenticationServiceAuthentication.LocalPublicKey
|
||||
};
|
||||
AuthenticationData authenticationData = new AuthenticationData();
|
||||
authenticationData.Data = systemAuthenticationServiceAuthentication.CalculateAuthenticationData(systemAuthenticationServiceAuthentication.LocalPublicKey, systemAuthenticationServiceAuthentication.RemotePublicKey, out var initializationVector);
|
||||
authenticationData.InitializationVector = initializationVector;
|
||||
ArchestrAResult result = ArchestrAResult.MakeGoodResult();
|
||||
systemAuthenticationServiceAuthentication.Lifetime = systemAuthenticationServiceAuthentication.SolutionParameters.ConnectionLifetime;
|
||||
ConnectResponse connectResponse = new ConnectResponse(result, servicePublicKey, authenticationData, systemAuthenticationServiceAuthentication.Lifetime + ":" + SystemAuthenticationConnectionBase.AsbAuthenticationVersion);
|
||||
systemAuthenticationServiceAuthentication.Sign(connectResponse, forceHmac: true);
|
||||
systemAuthenticationServiceAuthentication.ReasonSecureSessionNotEstablished = string.Empty;
|
||||
ServiceTrace.LogSuspend("SystemAuthenticationServiceAuthentication.ProcessClientConnection exit");
|
||||
return connectResponse;
|
||||
}
|
||||
|
||||
public bool ProcessClientAuthenticateMe(AuthenticateMeRequest request)
|
||||
{
|
||||
ServiceTrace.LogResume("SystemAuthenticationServiceAuthentication.ProcessClientAuthenticateMe entry with connection Id {0}", request.ConnectionValidator.ConnectionId);
|
||||
ServiceTrace.LogVerbose("SystemAuthenticationServiceAuthentication: Processing client AuthenticateMe() call for connection id {0}", request.ConnectionValidator.ConnectionId);
|
||||
if (!ValidRequest(request, forceHmac: true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
AuthenticationData consumerAuthenticationData = request.ConsumerAuthenticationData;
|
||||
if (consumerAuthenticationData != null)
|
||||
{
|
||||
byte[] expected = ReCalculateAuthenticationData(RemotePublicKey, LocalPublicKey, consumerAuthenticationData.InitializationVector);
|
||||
if (consumerAuthenticationData.AreEqual(expected))
|
||||
{
|
||||
ServiceTrace.LogVerbose("SystemAuthenticationServiceAuthentication: AuthenticateMe() authenticated client");
|
||||
base.SecureSessionEstablished = true;
|
||||
}
|
||||
}
|
||||
ServiceTrace.LogSuspend("SystemAuthenticationServiceAuthentication.ProcessClientAuthenticateMe exit");
|
||||
return base.SecureSessionEstablished;
|
||||
}
|
||||
|
||||
public static RenewResponse ProcessClientRenew(RenewRequest request)
|
||||
{
|
||||
ServiceTrace.LogResume("SystemAuthenticationServiceAuthentication.ProcessClientRenew entry with connection Id {0}", request.ConnectionValidator.ConnectionId);
|
||||
SysAuthenticatorServiceCache.GetServiceAuthenticator(request.ConnectionValidator.ConnectionId)?.ValidRequest(request, forceHmac: false);
|
||||
ServiceTrace.LogSuspend("SystemAuthenticationServiceAuthentication.ProcessClientRenew exit");
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void ProcessClientUpdateSystemAuthenticationConfiguration(UpdateSystemAuthenticationConfigurationRequest request)
|
||||
{
|
||||
ServiceTrace.LogResume("SystemAuthenticationServiceAuthentication.ProcessClientUpdateSystemAuthenticationConfiguration entry with connection Id {0}", request.ConnectionValidator.ConnectionId);
|
||||
SysAuthenticatorServiceCache.GetServiceAuthenticator(request.ConnectionValidator.ConnectionId)?.ValidRequest(request, forceHmac: false);
|
||||
ServiceTrace.LogSuspend("SystemAuthenticationServiceAuthentication.ProcessClientUpdateSystemAuthenticationConfiguration exit");
|
||||
}
|
||||
|
||||
public void ProcessClientDisconnect(DisconnectRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ServiceTrace.LogResume("SystemAuthenticationServiceAuthentication.ProcessClientDisconnect entry with connection Id {0}", request.ConnectionValidator.ConnectionId);
|
||||
AuthenticationData consumerAuthenticationData = request.ConsumerAuthenticationData;
|
||||
if (consumerAuthenticationData != null)
|
||||
{
|
||||
byte[] bytes = Decypher(consumerAuthenticationData.Data, consumerAuthenticationData.InitializationVector);
|
||||
if (Guid.TryParse(Encoding.UTF8.GetString(bytes), out var result) && result == base.ConnectionId)
|
||||
{
|
||||
SysAuthenticatorServiceCache.RemoveServiceAuthenticator(result);
|
||||
}
|
||||
}
|
||||
ServiceTrace.LogSuspend("SystemAuthenticationServiceAuthentication.ProcessClientDisconnect exit");
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
using System.ServiceModel;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[MessageContract(WrapperName = "UpdateSystemAuthenticationConfiguration", WrapperNamespace = "urn:msg.asb.se:2", IsWrapped = true)]
|
||||
[XmlRoot(Namespace = "urn:msg.asb.se:2")]
|
||||
public class UpdateSystemAuthenticationConfigurationRequest : ConnectedRequest
|
||||
{
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 0)]
|
||||
[XmlElement(ElementName = "EncryptedConfigurationData")]
|
||||
public string EncryptedConfigurationData { get; set; }
|
||||
|
||||
[MessageBodyMember(Namespace = "urn:msg.asb.se:2", Order = 1)]
|
||||
[XmlElement(ElementName = "InitializationVector", DataType = "base64Binary")]
|
||||
public byte[] InitializationVector { get; set; }
|
||||
|
||||
public UpdateSystemAuthenticationConfigurationRequest()
|
||||
{
|
||||
}
|
||||
|
||||
public UpdateSystemAuthenticationConfigurationRequest(string encryptedConfigurationData, byte[] initializationVector)
|
||||
{
|
||||
EncryptedConfigurationData = encryptedConfigurationData;
|
||||
InitializationVector = initializationVector;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
public class UserToken
|
||||
{
|
||||
public ushort? Encryption { get; set; }
|
||||
|
||||
public string HostName { get; set; }
|
||||
|
||||
public ushort? IdType { get; set; }
|
||||
|
||||
public string LocationId { get; set; }
|
||||
|
||||
public string UserName { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public ushort? Validity { get; set; }
|
||||
|
||||
public string JwtToken { get; set; }
|
||||
|
||||
public byte[] SamlToken { get; set; }
|
||||
|
||||
public byte[] X509Certificate { get; set; }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Asb.Base.V2;
|
||||
|
||||
[Serializable]
|
||||
[XmlType(Namespace = "urn:data.asb.se:2", IncludeInSchema = false)]
|
||||
public enum ValueItemType
|
||||
{
|
||||
None,
|
||||
Boolean,
|
||||
Byte,
|
||||
SignedByte,
|
||||
Short,
|
||||
UnsignedShort,
|
||||
Int,
|
||||
UnsignedInt,
|
||||
Long,
|
||||
UnsignedLong,
|
||||
Single,
|
||||
Double,
|
||||
Decimal,
|
||||
DateTime,
|
||||
Duration,
|
||||
Guid,
|
||||
String,
|
||||
MxLegacy
|
||||
}
|
||||
Reference in New Issue
Block a user