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)`
This commit is contained in:
2024-05-29 00:19:30 +03:00
parent af8efcbf4a
commit 920148e05b
7 changed files with 149 additions and 2 deletions
@@ -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;
}
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ public sealed class Core
/// <summary>
/// The Core API version.
/// </summary>
public UInt32 ApiVersion => 1;
public UInt32 ApiVersion => 2;
/// <summary>
/// The array that contains all modules instances.
+5
View File
@@ -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;
@@ -23,6 +25,9 @@ public abstract class Module
{
}
public virtual Task StartAsync(CancellationToken cts, IServiceScope scope) => Task.CompletedTask;
public virtual Task StopAsync(CancellationToken cts, IServiceScope scope) => Task.CompletedTask;
/// <summary>
/// Ensures if module is loaded and started. Note that call can change module event chain.
/// </summary>
+49
View File
@@ -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<string, Type> _allowedTypes = new Dictionary<string, Type>();
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<T>(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];
}
}
}
+58
View File
@@ -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<SerializationBinder>();
var allowedSerializationTypes = _core.Modules
.SelectMany(module => module.GetType().Assembly.ExportedTypes)
.Where(definedType => definedType.GetAttribute<AllowedSerializableTypeAttribute>() != null)
.ToDictionary(type => type.GetAttribute<AllowedSerializableTypeAttribute>().Name);
foreach (var pair in allowedSerializationTypes)
{
binder.RegisterAllowedType(pair.Key, pair.Value);
}
return Task.CompletedTask;
}
}
}
+6
View File
@@ -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<ConcurrentBag<DeleteRequest>>();
services.AddHostedService<DeleteService>();
services.AddHostedService<CoreManager>();
services.AddScoped<IOptionProvider, DbOptionProvider>();
services.AddHttpClient();
services.AddTelegramBotParameterParser<Uri, AbsoluteUriParser>();
services.AddSingleton<Hash>();
services.AddSingleton<ISerializationBinder, SerializationBinder>();
// Trigger module handlers.
_core.Modules.ConfigureServices(services);
+15
View File
@@ -0,0 +1,15 @@
using BotFramework;
using System;
namespace Kruzya.TelegramBot.Core
{
public class AbsoluteUriParser : IParameterParser<Uri>
{
public Uri DefaultInstance() => null;
public bool TryGetValue(string text, out Uri result)
{
return Uri.TryCreate(text, UriKind.Absolute, out result);
}
}
}