From 920148e05b2f71da38ede06e617d6bd7cfe1485c Mon Sep 17 00:00:00 2001 From: Sergey Gut Date: Wed, 29 May 2024 00:19:30 +0300 Subject: [PATCH] add Uri parser, add ISerializationBinder ISerializationBinder is added only in whitelist mode. Types for whitelisting should be registered manually via new API `Module.StartAsync()` or attribute `AllowedSerializableType(name)` --- .../AllowedSerializableTypeAttribute.cs | 14 +++++ Core/Core.cs | 2 +- Core/Module.cs | 7 ++- Core/SerializationBinder.cs | 49 ++++++++++++++++ Core/Service/CoreManager.cs | 58 +++++++++++++++++++ Core/Startup.cs | 6 ++ Core/UriBotParser.cs | 15 +++++ 7 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 Core/Attributes/AllowedSerializableTypeAttribute.cs create mode 100644 Core/SerializationBinder.cs create mode 100644 Core/Service/CoreManager.cs create mode 100644 Core/UriBotParser.cs diff --git a/Core/Attributes/AllowedSerializableTypeAttribute.cs b/Core/Attributes/AllowedSerializableTypeAttribute.cs new file mode 100644 index 0000000..0a5943f --- /dev/null +++ b/Core/Attributes/AllowedSerializableTypeAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace Kruzya.TelegramBot.Core.Attributes +{ + [AttributeUsage(AttributeTargets.Class)] + public class AllowedSerializableTypeAttribute : Attribute + { + public readonly string Name; + public AllowedSerializableTypeAttribute(string name) + { + Name = name; + } + } +} diff --git a/Core/Core.cs b/Core/Core.cs index b2e7cf9..fb729a1 100644 --- a/Core/Core.cs +++ b/Core/Core.cs @@ -14,7 +14,7 @@ public sealed class Core /// /// The Core API version. /// - public UInt32 ApiVersion => 1; + public UInt32 ApiVersion => 2; /// /// The array that contains all modules instances. diff --git a/Core/Module.cs b/Core/Module.cs index d9c422b..8c2e556 100644 --- a/Core/Module.cs +++ b/Core/Module.cs @@ -2,6 +2,8 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using System.Threading; +using System.Threading.Tasks; namespace Kruzya.TelegramBot.Core; @@ -22,7 +24,10 @@ public abstract class Module public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } - + + public virtual Task StartAsync(CancellationToken cts, IServiceScope scope) => Task.CompletedTask; + public virtual Task StopAsync(CancellationToken cts, IServiceScope scope) => Task.CompletedTask; + /// /// Ensures if module is loaded and started. Note that call can change module event chain. /// diff --git a/Core/SerializationBinder.cs b/Core/SerializationBinder.cs new file mode 100644 index 0000000..8457755 --- /dev/null +++ b/Core/SerializationBinder.cs @@ -0,0 +1,49 @@ +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Kruzya.TelegramBot.Core +{ + public class SerializationBinder : ISerializationBinder + { + protected IDictionary _allowedTypes = new Dictionary(); + + public void RegisterAllowedType(string name, Type type) + { + if (_allowedTypes.ContainsKey(name)) + { + throw new ArgumentException($"{name} is already registered", "name"); + } + + foreach (var knownType in _allowedTypes.Values) + { + if (type != knownType) + { + continue; + } + + throw new ArgumentException($"{type.FullName} is already known type", "type"); + } + + _allowedTypes.Add(name, type); + } + + public void RegisterAllowedType(string name) where T : class + { + RegisterAllowedType(name, typeof(T)); + } + + public void BindToName(Type serializedType, out string assemblyName, out string typeName) + { + assemblyName = null; + typeName = _allowedTypes.Where(x => x.Value == serializedType) + .FirstOrDefault().Key; + } + + public Type BindToType(string assemblyName, string typeName) + { + return _allowedTypes[typeName]; + } + } +} diff --git a/Core/Service/CoreManager.cs b/Core/Service/CoreManager.cs new file mode 100644 index 0000000..b082b6b --- /dev/null +++ b/Core/Service/CoreManager.cs @@ -0,0 +1,58 @@ +using Castle.Core.Internal; +using Kruzya.TelegramBot.Core.Attributes; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using System.Linq; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; + +namespace Kruzya.TelegramBot.Core.Service +{ + public class CoreManager : IHostedService + { + private readonly Core _core; + private readonly IServiceScopeFactory _scopeFactory; + + public CoreManager(Core core, IServiceScopeFactory scopeFactory) + { + _core = core; + _scopeFactory = scopeFactory; + } + + public async Task StartAsync(CancellationToken cts) + { + await ManagerStartAsync(cts, _scopeFactory.CreateScope()); + foreach (var module in _core.Modules) + { + await module.StartAsync(cts, _scopeFactory.CreateScope()); + } + } + + public async Task StopAsync(CancellationToken cts) + { + // await ManagerStopAsync(cts, _scopeFactory.CreateScope()); + foreach (var module in _core.Modules) + { + await module.StopAsync(cts, _scopeFactory.CreateScope()); + } + } + + public Task ManagerStartAsync(CancellationToken cts, IServiceScope scope) + { + var binder = scope.ServiceProvider.GetRequiredService(); + var allowedSerializationTypes = _core.Modules + .SelectMany(module => module.GetType().Assembly.ExportedTypes) + .Where(definedType => definedType.GetAttribute() != null) + .ToDictionary(type => type.GetAttribute().Name); + + foreach (var pair in allowedSerializationTypes) + { + binder.RegisterAllowedType(pair.Key, pair.Value); + } + + return Task.CompletedTask; + } + } +} diff --git a/Core/Startup.cs b/Core/Startup.cs index b81117e..f5d230a 100644 --- a/Core/Startup.cs +++ b/Core/Startup.cs @@ -14,6 +14,8 @@ using System.Collections.Concurrent; using Kruzya.TelegramBot.Core.Options; using Telegram.Bot.Types; using BotFramework.Abstractions.UpdateProvider; +using System; +using Newtonsoft.Json.Serialization; namespace Kruzya.TelegramBot.Core; @@ -55,11 +57,15 @@ public class Startup services.AddSingleton>(); services.AddHostedService(); + services.AddHostedService(); services.AddScoped(); services.AddHttpClient(); + services.AddTelegramBotParameterParser(); + services.AddSingleton(); + services.AddSingleton(); // Trigger module handlers. _core.Modules.ConfigureServices(services); diff --git a/Core/UriBotParser.cs b/Core/UriBotParser.cs new file mode 100644 index 0000000..9176687 --- /dev/null +++ b/Core/UriBotParser.cs @@ -0,0 +1,15 @@ +using BotFramework; +using System; + +namespace Kruzya.TelegramBot.Core +{ + public class AbsoluteUriParser : IParameterParser + { + public Uri DefaultInstance() => null; + + public bool TryGetValue(string text, out Uri result) + { + return Uri.TryCreate(text, UriKind.Absolute, out result); + } + } +}