using Host.Application.Stories; using Knot.Shared.Kernel; using Knot.Shared.Kernel.Configuration; using Knot.Shared.Kernel.Constants; using System; using System.Linq; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using MediatR; namespace Host.Application.Federation.Commands; public record HandshakeRequest(string Domain, string PublicKey); public record HandshakeResponse(string Domain, string PublicKey, string Status); public record HandshakeFederationCommand(HandshakeRequest Request) : ICommand; internal sealed class HandshakeFederationCommandHandler : ICommandHandler { private readonly ISettingsService _settings; public HandshakeFederationCommandHandler(ISettingsService settings) { _settings = settings; } public Task> Handle(HandshakeFederationCommand request, CancellationToken cancellationToken) { var conf = _settings.Current; if (!conf.EnableConfederation) return Task.FromResult(Result.Failure(new Error(Errors.DisabledByAdmin, "Federation is disabled"))); if (string.IsNullOrEmpty(request.Request.Domain) || string.IsNullOrEmpty(request.Request.PublicKey)) return Task.FromResult(Result.Failure(DomainErrors.RequestInvalid)); var allowedList = conf.AllowedDomains?.Select(d => d.Trim().ToLower()).ToList() ?? new System.Collections.Generic.List(); if (!allowedList.Contains(request.Request.Domain.ToLowerInvariant())) return Task.FromResult(Result.Failure(StoryErrors.Unauthorized)); using var rsa = RSA.Create(2048); var selfPublicKey = Convert.ToBase64String(rsa.ExportRSAPublicKey()); var response = new HandshakeResponse( Environment.GetEnvironmentVariable("DOMAIN") ?? "knot.local", selfPublicKey, "Accepted" ); return Task.FromResult(Result.Success(response)); } }