ValueTask <ISender> ILocalParty.CreateSenderAsync(IActivityMonitor monitor, ITrustedParty audience, TimeSpan validity, bool encrypt) { TrustedParty?p; Throw.CheckArgument((p = audience as TrustedParty) != null); return(CreateSenderAsync(monitor, p, validity, encrypt)); }
static GenerateCodeResult DoGenerateCode(StObjCollectorResult result, Func <StObjEngineConfiguration, StObjEngineConfiguration>?engineConfigurator, bool generateSourceFiles, CompileOption compileOption) { Throw.CheckArgument(!result.HasFatalError); var assemblyName = StObjContextRoot.GeneratedAssemblyName + DateTime.Now.ToString(".yyMdHmsffff"); var config = new StObjEngineConfiguration() { GeneratedAssemblyName = assemblyName, }; config.BinPaths.Add(new BinPathConfiguration() { CompileOption = compileOption, GenerateSourceFiles = generateSourceFiles, ProjectPath = TestHelper.TestProjectFolder }); if (engineConfigurator != null) { config = engineConfigurator.Invoke(config); Throw.CheckState("The engine configuration returned by the engineConfigurator cannot be null.", config != null); } return(new GenerateCodeResult(result, Setup.StObjEngine.Run(TestHelper.Monitor, result, config))); }
/// <summary> /// Opens a <see cref="LogReader"/> to read the content of a compressed or uncompressed stream. /// The stream will be closed when <see cref="LogReader.Dispose"/> will be called. /// </summary> /// <param name="seekableStream">Stream that must support Seek operations (<see cref="Stream.CanSeek"/> must be true).</param> /// <param name="dataOffset"> /// An optional offset where the stream position must be initially set: this is the position of an entry in the actual (potentially uncompressed stream), /// not the offset in the original stream. /// </param> /// <param name="filter">An optional <see cref="MulticastFilter"/>.</param> /// <returns>A <see cref="LogReader"/> that will close the file when disposed.</returns> /// <remarks> /// .ckmon files exist in different file versions, depending on headers. /// The file can be compressed using GZipStream, in which case the header will be the magic GZIP header: 1F 8B. /// New header (applies to version 5), the file will start with 43 4B 4D 4F 4E (CKMON in ASCII), followed by the version number, instead of only the version number. /// </remarks> public static LogReader Open(Stream seekableStream, long dataOffset = 0, MulticastFilter?filter = null) { Throw.CheckNotNullArgument(seekableStream); Throw.CheckArgument(seekableStream.CanSeek); LogReaderStreamInfo i = LogReaderStreamInfo.OpenStream(seekableStream); var s = i.LogStream; if (dataOffset > 0) { if (s.CanSeek) { s.Seek(dataOffset, SeekOrigin.Current); } else { var buffer = new byte[8192]; int toRead; while ((toRead = (int)Math.Min(8192, dataOffset)) > 0 && s.Read(buffer, 0, toRead) == toRead) { dataOffset -= toRead; } } } var r = new LogReader(s, i.Version, i.HeaderLength) { CurrentFilter = filter }; return(r); }
/// <summary> /// Initializes a new <see cref="KeyRequirement"/> for RSA or symmetric keys. /// </summary> /// <param name="keyType">Type of the key (<see cref="KeyType.Rsa"/> or <see cref="KeyType.Oct"/>).</param> /// <param name="operations">Supported key operations.</param> /// <param name="keySizeInBits">Size of the key.</param> /// <param name="initiatorAlgorithmName">See <see cref="InitiatorAlgorithmName"/>.</param> public KeyRequirement(KeyType keyType, KeyOperations operations, int keySizeInBits, string?initiatorAlgorithmName = null) { Throw.CheckArgument(keyType != KeyType.Ec); KeyType = keyType; Operations = operations; KeySizeInBits = keySizeInBits; InitiatorAlgorithmName = initiatorAlgorithmName; }
public static StObjEngineResult Run(IActivityMonitor monitor, StObjCollectorResult result, StObjEngineConfiguration config) { Throw.CheckNotNullArgument(monitor); Throw.CheckNotNullArgument(result); Throw.CheckNotNullArgument(config); Throw.CheckArgument(config.BinPaths.Select(b => b.Path).Distinct().Count() == 1); var e = new StObjEngine(monitor, config); return(e.Run(new MonoResolver(result))); }
public KeyExchangeFailedEventArgs(IActivityMonitor monitor, ITrustedParty targetParty, KeyExchangeResultCode resultCode) : base(monitor) { Throw.CheckNotNullArgument(targetParty); Throw.CheckArgument(resultCode != KeyExchangeResultCode.Success); TargetParty = targetParty; ResultCode = resultCode; }
/// <summary> /// Initializes a new <see cref="RemoteIdentityOptions"/>. /// </summary> /// <param name="remoteDomainNameOverride">See <see cref="RemoteDomainNameOverride"/>.</param> /// <param name="remoteEnvironmentNameOverride">See <see cref="RemoteEnvironmentNameOverride"/>.</param> /// <param name="remotePartyNameOverride">See <see cref="RemotePartyNameOverride"/>.</param> public RemoteIdentityOptions(string?remoteDomainNameOverride = null, string?remoteEnvironmentNameOverride = null, string?remotePartyNameOverride = null) { Throw.CheckArgument(remoteDomainNameOverride == null || CoreApplicationIdentity.Builder.IsValidIdentifier(remoteDomainNameOverride)); Throw.CheckArgument(string.IsNullOrEmpty(remoteEnvironmentNameOverride) || CoreApplicationIdentity.Builder.IsValidIdentifier(remoteEnvironmentNameOverride)); Throw.CheckArgument(remotePartyNameOverride == null || Base64UrlHelper.IsBase64Url(remotePartyNameOverride)); RemoteDomainNameOverride = remoteDomainNameOverride; RemoteEnvironmentNameOverride = remoteEnvironmentNameOverride; RemotePartyNameOverride = remotePartyNameOverride; }
/// <summary> /// Attempts to load a StObjMap from an assembly name. /// <para> /// If a <see cref="SuffixSignature"/> file exists and contains a valid signature, the StObjMap is /// loaded from the <see cref="GetAvailableMapInfos(IActivityMonitor?)"/> if it exists. /// </para> /// </summary> /// <param name="assemblyName">The assembly name.</param> /// <param name="monitor">Optional monitor to use.</param> /// <returns>A <see cref="IStObjMap"/> that provides access to the objects graph.</returns> public static IStObjMap?Load(string assemblyName, IActivityMonitor?monitor = null) { Throw.CheckNotNullOrEmptyArgument(assemblyName); Throw.CheckArgument(FileUtil.IndexOfInvalidFileNameChars(assemblyName) < 0); if (!assemblyName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && !assemblyName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) { assemblyName = assemblyName + ".dll"; } string assemblyFullPath = Path.Combine(AppContext.BaseDirectory, assemblyName); var signaturePath = assemblyFullPath + SuffixSignature; if (File.Exists(signaturePath) && SHA1Value.TryParse(File.ReadAllText(signaturePath), out var signature)) { var map = Load(signature, monitor); if (map != null) { return(map); } } lock ( _alreadyHandled ) { LockedEnsureMonitor(ref monitor); using (monitor.OpenInfo($"Loading StObj map from '{assemblyName}'.")) { try { // LoadFromAssemblyPath caches the assemblies by their path. // No need to do it. var a = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyFullPath); var info = LockedGetMapInfo(a, ref monitor); if (info == null) { return(null); } return(LockedGetStObjMapFromInfo(info, ref monitor)); } catch (Exception ex) { monitor.Error(ex); return(null); } } } }
public async ValueTask <ISender> CreateSenderAsync(IActivityMonitor monitor, TrustedParty audience, ISignAlgorithm algorithm, TimeSpan validity, bool encrypt = false, uint maxUseCount = 0) { Throw.CheckNotNullArgument(monitor); Throw.CheckNotNullArgument(audience); Throw.CheckNotNullArgument(algorithm); Throw.CheckArgument(validity >= Configuration.MinSignatureLifetime && validity <= Configuration.MaxSignatureLifetime); var bestBefore = _appClock.Clock.UtcNow + validity; var key = algorithm.KeyRequirement != null ? await ResolveOutgoingKeyAsync(monitor, audience, algorithm.KeyRequirement, bestBefore, maxUseCount) : null; return(new Sender(this, audience, algorithm, key, bestBefore)); }
/// <summary> /// Initializes a new <see cref="TrustedPartyConfiguration"/>. /// </summary> /// <param name="outgoingSignatureAlgorithmId">See <see cref="OutgoingSignatureAlgorithm"/>.</param> /// <param name="allowedIncomingSignatureAlgorithms">See <see cref="AllowedIncomingSignatureAlgorithms"/>.</param> /// <param name="minSignatureLifetime">See <see cref="MinSignatureLifetime"/>.</param> /// <param name="maxSignatureLifetime"><see cref="MaxSignatureLifetime"/>.</param> /// <param name="alwaysRequireIncomingExpirationTime">See <see cref="AlwaysRequireIncomingExpirationTime"/>.</param> /// <param name="rsaKeySizeForSignature">See <see cref="RSAKeyBitLengthForSignature"/>.</param> public TrustedPartyConfiguration(SignatureAlgorithmId outgoingSignatureAlgorithmId, IEnumerable <SignatureAlgorithmId>?allowedIncomingSignatureAlgorithms, TimeSpan minSignatureLifetime, TimeSpan maxSignatureLifetime, bool alwaysRequireIncomingExpirationTime, int rsaKeySizeForSignature = 2048) { Throw.CheckArgument(Enum.IsDefined(typeof(SignatureAlgorithmId), outgoingSignatureAlgorithmId)); Throw.CheckArgument(rsaKeySizeForSignature is 2048 or 3072 or 7680 or 15360); Throw.CheckArgument(minSignatureLifetime >= TimeSpan.FromSeconds(1)); Throw.CheckArgument(maxSignatureLifetime <= TimeSpan.FromDays(1095)); Throw.CheckArgument(minSignatureLifetime <= maxSignatureLifetime); _rsaKaySize = rsaKeySizeForSignature; _minSignatureLifetime = minSignatureLifetime; _maxSignatureLifetime = maxSignatureLifetime; _alwaysRequireIncomingExpirationTime = alwaysRequireIncomingExpirationTime; _algCache = new ISignAlgorithm[14]; _outgoingSignAlg = CreateAndCacheSignAlgorithm(outgoingSignatureAlgorithmId); if (allowedIncomingSignatureAlgorithms != null) { foreach (var s in allowedIncomingSignatureAlgorithms) { _allowedIncomingSignAlgorithms |= 1 << (int)s; CreateAndCacheSignAlgorithm(s); } } else { _allowedIncomingSignAlgorithms = 1 << (int)outgoingSignatureAlgorithmId; } ISignAlgorithm CreateAndCacheSignAlgorithm(SignatureAlgorithmId id) { var a = _algCache[(int)id]; if (a == null) { a = id.GetAlgorithm(_rsaKaySize); _algCache[(int)id] = a; } return(a); } }
/// <summary> /// Sets <see cref="AutoServiceKind"/> combination (that must not be <see cref="AutoServiceKind.None"/>) for a type. /// Can be called multiple times as long as no contradictory registration already exists (for instance, /// a <see cref="IRealObject"/> cannot be a Front service). /// Note that <see cref="AutoServiceKind.IsFrontService"/> is automatically expanded with <see cref="AutoServiceKind.IsScoped"/> /// and <see cref="AutoServiceKind.IsFrontProcessService"/>. /// </summary> /// <param name="m">The monitor.</param> /// <param name="t">The type to register.</param> /// <param name="kind">The kind of service. Must not be <see cref="AutoServiceKind.None"/>.</param> /// <returns>The type kind on success, null on error (errors - included combination ones - are logged).</returns> public CKTypeKind?SetAutoServiceKind(IActivityMonitor m, Type t, AutoServiceKind kind) { Throw.CheckArgument(kind != AutoServiceKind.None); bool hasFrontType = (kind & (AutoServiceKind.IsFrontProcessService | AutoServiceKind.IsFrontService)) != 0; bool hasLifetime = (kind & (AutoServiceKind.IsScoped | AutoServiceKind.IsSingleton)) != 0; bool hasMultiple = (kind & AutoServiceKind.IsMultipleService) != 0; CKTypeKind k = (CKTypeKind)kind; if (hasFrontType) { if ((kind & AutoServiceKind.IsFrontService) != 0) { k |= CKTypeKind.IsScoped; hasLifetime = true; } k |= CKTypeKind.IsFrontProcessService; } string?error = k.GetCombinationError(t.IsClass); if (error != null) { m.Error($"Invalid Auto Service kind registration '{k.ToStringFlags()}' for type '{t}'."); return(null); } if (hasLifetime) { k |= IsLifetimeReasonExternal; } if (hasMultiple) { k |= IsMultipleReasonExternal; } if (hasFrontType) { k |= IsFrontTypeReasonExternal; } return(SetLifetimeOrFrontType(m, t, k)); }
/// <summary> /// Helper that copy a directory content to another one with retries. /// This first deletes the target directory before copying the content. /// </summary> /// <param name="monitor">The monitor to use.</param> /// <param name="source">The source directory.</param> /// <param name="target">The target directory.</param> /// <returns>True on success, false on error.</returns> public static bool SafeCopy(IActivityMonitor monitor, NormalizedPath source, NormalizedPath target) { Throw.CheckNotNullArgument(monitor); Throw.CheckArgument(!target.StartsWith(source) && !source.StartsWith(target)); int tryCount = 0; retry: try { var dS = new DirectoryInfo(source); var dT = new DirectoryInfo(target); if (!dS.Exists) { monitor.Error($"Source directory '{dS.FullName}' not found."); return(false); } if (!dT.Exists) { Directory.CreateDirectory(dT.FullName); } else { dT.Delete(recursive: true); } FileUtil.CopyDirectory(dS, dT); } catch (Exception ex) { if (++tryCount > 5) { monitor.Error($"Failed to copy directory content from {source} to '{target}' after 5 tries.", ex); return(false); } monitor.Warn($"Error while copying directory content. Retrying in {tryCount * 50} ms.", ex); System.Threading.Thread.Sleep(tryCount * 50); goto retry; } monitor.Info($"Directory content copied from '{source}' to '{target}'."); return(true); }
public LocalParty(string domainName, string environmentName, string partyName, StandardKeysConfiguration keysConfiguration, IApplicationIdentityClock clock, IKeyStore?keyStore, TrustedPartyConfiguration?localConfiguration = null) : base(domainName, environmentName, partyName, localConfiguration) { Throw.CheckArgument(domainName != null && CoreApplicationIdentity.Builder.IsValidIdentifier(domainName)); Throw.CheckArgument(environmentName != null && (environmentName == "" || CoreApplicationIdentity.Builder.IsValidIdentifier(environmentName))); Throw.CheckArgument(partyName != null && Base64UrlHelper.IsBase64Url(partyName)); _keyExchangeRequired = new PerfectEventSender <KeyExchangeRequiredEventArgs>(); _remotes = new ConcurrentDictionary <string, RemoteParty>(); _exposed = _remotes.AsIReadOnlyDictionary <string, RemoteParty, IRemoteParty>(); KeyFactory = new KeyFactory(keysConfiguration, clock.Clock); KeyStore = keyStore; DomainName = domainName; EnvironmentName = environmentName; _appClock = clock; }
/// <summary> /// Initializes a new remote and adds it to the <see cref="RemoteParties"/>. /// If a remote party with the same name must already exists it must be identically configured /// (except the <paramref name="initialOutgoingOneTimePassword"/>) otherwise an <see cref="ArgumentException"/> is thrown. /// </summary> /// <param name="partyName">This remote <see cref="ITrustedParty.PartyName"/>.</param> /// <param name="uri">See <see cref="IRemoteParty.Uri"/>.</param> /// <param name="initialOutgoingOneTimePassword">Optional configured one time password to use.</param> /// <param name="keyScope">See <see cref="IRemoteParty.PersistKeys"/>.</param> /// <param name="externalIdentityOptions">Optional identity options.</param> public Task <IRemoteParty> EnsureRemotePartyAsync(IActivityMonitor monitor, string partyName, Uri?uri, OneTimePassword?initialOutgoingOneTimePassword = null, RemotePartyKeyScope keyScope = RemotePartyKeyScope.LocalIdentity, RemoteIdentityOptions?externalIdentityOptions = null, TrustedPartyConfiguration?partyConfiguration = null) { bool exists = false; var p = _remotes.AddOrUpdate(partyName, n => new RemoteParty(this, partyName, keyScope, uri, externalIdentityOptions, partyConfiguration), (n, already) => { Throw.CheckArgument(already.Uri == uri); Throw.CheckArgument(already.KeyScope == keyScope); Throw.CheckArgument(already.IdentityOptions?.RemoteDomainNameOverride == externalIdentityOptions?.RemoteDomainNameOverride); Throw.CheckArgument(already.IdentityOptions?.RemoteEnvironmentNameOverride == externalIdentityOptions?.RemoteEnvironmentNameOverride); Throw.CheckArgument(already.IdentityOptions?.RemotePartyNameOverride == externalIdentityOptions?.RemotePartyNameOverride); exists = true; // We consider here that the call to "Ensure" should honor its semantics... if (initialOutgoingOneTimePassword != null) { already.OneTimePassword = initialOutgoingOneTimePassword; } return(already); }); if (!exists) { if (initialOutgoingOneTimePassword != null) { p.OneTimePassword = initialOutgoingOneTimePassword; } monitor.Info($"Created new RemoteParty '{p}'."); } return(Task.FromResult <IRemoteParty>(p)); }
/// <summary> /// Initializes a new <see cref="GeneratedFileArtifact"/>. /// </summary> /// <param name="filePath">File path. It MUST not be <see cref="NormalizedPath.IsEmptyPath"/> otherwise an <see cref="ArgumentException"/> is thrown.</param> public GeneratedFileArtifact(NormalizedPath filePath) { Throw.CheckArgument(!filePath.IsEmptyPath); Path = filePath; }
/// <summary> /// Gets the conflicting duplicate status message or null if this CK type kind is valid. /// </summary> /// <param name="this">This CK type kind.</param> /// <param name="isClass">True for Class type (not for interface).</param> /// <returns>An error message or null.</returns> public static string?GetCombinationError(this CKTypeKind @this, bool isClass) { Throw.CheckArgument(@this >= 0 && @this <= CKTypeKindDetector.MaskPublicInfo); // Pure predicates: checks are made against them. bool isAuto = (@this & CKTypeKind.IsAutoService) != 0; bool isScoped = (@this & CKTypeKind.IsScoped) != 0; bool isSingleton = (@this & CKTypeKind.IsSingleton) != 0; bool isRealObject = (@this & (CKTypeKind.RealObject & ~CKTypeKind.IsSingleton)) != 0; bool isPoco = (@this & CKTypeKind.IsPoco) != 0; bool isPocoClass = (@this & CKTypeKind.IsPocoClass) != 0; bool isFrontEndPoint = (@this & CKTypeKind.IsFrontService) != 0; bool isFrontProcess = (@this & CKTypeKind.IsFrontProcessService) != 0; bool isMarshallable = (@this & CKTypeKind.IsMarshallable) != 0; bool isMultiple = (@this & CKTypeKind.IsMultipleService) != 0; if (isFrontEndPoint && !isFrontProcess) { Throw.ArgumentException("CKTypeKind value error: missing IsFrontProcessService flag for IsFrontService: " + @this.ToStringFlags()); } if (isRealObject && !isSingleton) { Throw.Exception("CKTypeKind value error: missing IsSingleton flag to RealObject mask: " + @this.ToStringFlags()); } string?conflict = null; if (isPoco) { if (@this != CKTypeKind.IsPoco) { conflict = "Poco cannot be combined with any other aspect"; } if (isClass) { if (conflict != null) { conflict += ", "; } conflict += "A class cannot be a IPoco"; } } else if (isPocoClass) { if (@this != CKTypeKind.IsPocoClass) { conflict = "[PocoClass] class cannot be combined with any other aspect"; } } else if (isRealObject) { Debug.Assert(isSingleton, "Checked above."); if (@this != CKTypeKind.RealObject) { // If IsMultiple, then this is an interface, not a class: a IRealObject interface cannot be IsMultiple. if (isScoped) { conflict = "RealObject cannot have a Scoped lifetime"; } else if (isMultiple) { conflict = "IRealObject interface cannot be marked as a Multiple service"; } else if (isAuto && !isClass) { conflict = "IRealObject interface cannot be an IAutoService"; } // Always handle Front service. if (isMarshallable) { if (conflict != null) { conflict += ", "; } conflict += "RealObject cannot be marked as Marshallable"; } // Always handle Front service. if (isFrontEndPoint | isFrontProcess) { if (conflict != null) { conflict += ", "; } conflict += "RealObject cannot be a front service"; } } } else if (isScoped && isSingleton) { if (isFrontProcess) { conflict = "An interface or an implementation cannot be both Scoped and Singleton (since this is marked as a FrontService, this is de facto Scoped)"; } else { conflict = "An interface or an implementation cannot be both Scoped and Singleton"; } } if (isClass) { if ((@this & CKTypeKind.IsMultipleService) != 0) { conflict = "A class cannot be marked as a Multiple service: only interfaces can be IsMultiple."; } } return(conflict == null ? null : $"Invalid CK type combination: {conflict} for {(isClass ? "class" : "interface")} '{@this.ToStringFlags()}'."); }
/// <summary> /// Initializes a new <see cref="GeneratedFileArtifactWithTextSignature"/>. /// </summary> /// <param name="filePath">File path. It MUST not be <see cref="NormalizedPath.IsEmptyPath"/> otherwise an <see cref="ArgumentException"/> is thrown.</param> public GeneratedFileArtifactWithTextSignature(NormalizedPath filePath) : base(filePath) { Throw.CheckArgument(!filePath.IsEmptyPath); _signatureFile = filePath.RemoveLastPart().AppendPart(filePath.LastPart + StObjContextRoot.SuffixSignature); }
/// <summary> /// Initializes a new generated directory. /// </summary> /// <param name="path">Directory path. It MUST not be <see cref="NormalizedPath.IsEmptyPath"/> otherwise an <see cref="ArgumentException"/> is thrown.</param> public GeneratedDirectoryArtifact(NormalizedPath path) { Throw.CheckArgument(!path.IsEmptyPath); Path = path; }
/// <summary> /// Sets the <see cref="TargetParty"/>'s response. /// This can be called only once and only if <see cref="SetCommunicationResponseError()"/> has not been called yet. /// </summary> /// <param name="response">The remote's response as a binary blob.</param> public void SetResponse(ReadOnlyMemory <byte> response) { Throw.CheckState(!HasResponse); Throw.CheckArgument(!response.IsEmpty); Response = response; }