From d47292344273c18c7d442b4e472a67ea266d938c Mon Sep 17 00:00:00 2001 From: Meik Date: Wed, 1 Jul 2026 00:36:01 +0200 Subject: [PATCH] fix: isolate journal storage dependency in sandbox --- F4SDM42WebApi/F4SD - M42WebApi.csproj | 2 +- F4SDM42WebApi/F4SDM42WebApiController.cs | 64 +++++++++++++++++++++++- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/F4SDM42WebApi/F4SD - M42WebApi.csproj b/F4SDM42WebApi/F4SD - M42WebApi.csproj index 40a3eca..632e8ee 100644 --- a/F4SDM42WebApi/F4SD - M42WebApi.csproj +++ b/F4SDM42WebApi/F4SD - M42WebApi.csproj @@ -13,7 +13,7 @@ false 6673a25d-33d6-4072-91d9-abed4be1a966 C4ITF4SD*.* - 1.4.0.28 + 1.4.0.29 true diff --git a/F4SDM42WebApi/F4SDM42WebApiController.cs b/F4SDM42WebApi/F4SDM42WebApiController.cs index 148e42c..0d72fe5 100644 --- a/F4SDM42WebApi/F4SDM42WebApiController.cs +++ b/F4SDM42WebApi/F4SDM42WebApiController.cs @@ -43,6 +43,8 @@ namespace C4IT.F4SD private readonly F4SDHelperService _f4stHelperService; + private const string TeamsNotificationServiceTypeName = "Matrix42.Contracts.Platform.MsTeams.ITeamsNotificationService"; + private const string StorageServiceTypeName = "Matrix42.StorageService.Contracts.IStorageService"; public string BaseUrl => Request?.RequestUri == null ? string.Empty : $"{Request.RequestUri.Scheme}://{Request.RequestUri.Host}"; public string EndpointBaseUrl => $"{BaseUrl}/m42Services/api/c4itf4sdwebapi"; @@ -132,9 +134,9 @@ namespace C4IT.F4SD private object ResolveJournalServiceConstructorParameter(System.Reflection.ParameterInfo parameter) { if (parameter.ParameterType == typeof(IDependencyResolver)) - return _resolver; + return CreateJournalDependencyResolver(_resolver); - if (parameter.ParameterType.FullName == "Matrix42.Contracts.Platform.MsTeams.ITeamsNotificationService") + if (parameter.ParameterType.FullName == TeamsNotificationServiceTypeName || parameter.ParameterType.FullName == StorageServiceTypeName) return CreateNoOpInterfaceProxy(parameter.ParameterType); var service = _resolver.TryGet(parameter.ParameterType); @@ -144,6 +146,13 @@ namespace C4IT.F4SD throw new InvalidOperationException($"Required JournalService constructor dependency is not registered: {parameter.ParameterType.FullName} ({parameter.Name})."); } + private static IDependencyResolver CreateJournalDependencyResolver(IDependencyResolver resolver) + { + var proxy = DispatchProxy.Create(); + ((JournalDependencyResolverProxy)(object)proxy).Inner = resolver; + return proxy; + } + private static Type FindLoadedType(string fullName) { return AppDomain.CurrentDomain @@ -163,6 +172,57 @@ namespace C4IT.F4SD .Invoke(null, null); } + public class JournalDependencyResolverProxy : DispatchProxy + { + public IDependencyResolver Inner { get; set; } + + protected override object Invoke(MethodInfo targetMethod, object[] args) + { + var requestedType = GetRequestedServiceType(targetMethod, args); + if (requestedType?.FullName == StorageServiceTypeName) + return CreateResolverResultForStorageService(targetMethod, requestedType); + + try + { + return targetMethod.Invoke(Inner, args); + } + catch (TargetInvocationException ex) when (ex.InnerException != null) + { + throw ex.InnerException; + } + } + } + + private static Type GetRequestedServiceType(MethodInfo targetMethod, object[] args) + { + if (targetMethod.IsGenericMethod && targetMethod.GetGenericArguments().Length > 0) + return targetMethod.GetGenericArguments()[0]; + + if (args?.Length > 0 && args[0] is Type type) + return type; + + return null; + } + + private static object CreateResolverResultForStorageService(MethodInfo targetMethod, Type requestedType) + { + if (targetMethod.Name.StartsWith("IsRegistered", StringComparison.Ordinal)) + return true; + + if (targetMethod.Name.Contains("All")) + return CreateEmptyEnumerable(targetMethod.ReturnType, requestedType); + + return CreateNoOpInterfaceProxy(requestedType); + } + + private static object CreateEmptyEnumerable(Type returnType, Type itemType) + { + if (returnType.IsGenericType) + return Array.CreateInstance(returnType.GetGenericArguments()[0], 0); + + return Array.CreateInstance(itemType ?? typeof(object), 0); + } + public class NoOpInterfaceProxy : DispatchProxy { public NoOpInterfaceProxy()