Files

109 lines
4.3 KiB
C#
Raw Permalink Normal View History

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using PrivaPub.Federation.Signing;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json.Nodes;
namespace PrivaPub.Tests.Support
{
public sealed class RemoteActor
{
readonly RSA _key = RSA.Create(2048);
public RemoteActor(Peer peer, string name, string origin = default, string type = "Person")
{
Name = $"{name}{Guid.NewGuid():N}"[..20];
Id = $"{origin ?? peer.A}/users/{Name}";
Type = type;
peer.Serve($"/users/{Name}", Document().ToJsonString());
}
public string Type { get; }
public string Name { get; }
public string Id { get; }
public string KeyId => Id + "#main-key";
public string SharedInbox => Id.Split("/users/")[0] + "/inbox";
public JsonObject Document() => new()
{
["id"] = Id,
["type"] = Type,
["preferredUsername"] = Name,
["inbox"] = Id + "/inbox",
["followers"] = Id + "/followers",
["publicKey"] = new JsonObject
{
["id"] = KeyId,
["owner"] = Id,
["publicKeyPem"] = _key.ExportSubjectPublicKeyInfoPem()
}
};
2026-10-03 10:43:03 +02:00
public HttpRequestMessage SignedGet(string path, string host = "privapub.test")
{
var date = DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture);
2026-10-03 10:43:03 +02:00
var request = new HttpRequestMessage(HttpMethod.Get, path);
request.Headers.TryAddWithoutValidation("Date", date);
request.Headers.TryAddWithoutValidation("Accept", "application/activity+json");
request.Headers.TryAddWithoutValidation("Signature", Signature($"(request-target): get {path}\nhost: {host}\ndate: {date}", "(request-target) host date"));
return request;
}
public HttpRequestMessage SignedPost(string path, JsonNode activity, string host = "privapub.test", string date = default)
{
var body = Encoding.UTF8.GetBytes(activity.ToJsonString());
date ??= DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture);
var digest = HttpSignatures.Digest(body);
var request = new HttpRequestMessage(HttpMethod.Post, path) { Content = new ByteArrayContent(body) };
request.Content.Headers.TryAddWithoutValidation("Content-Type", "application/activity+json");
request.Headers.TryAddWithoutValidation("Date", date);
request.Headers.TryAddWithoutValidation("Digest", digest);
request.Headers.TryAddWithoutValidation("Signature",
Signature($"(request-target): post {path}\nhost: {host}\ndate: {date}\ndigest: {digest}", "(request-target) host date digest"));
return request;
}
string Signature(string signingString, string headers)
{
var signature = Convert.ToBase64String(_key.SignData(Encoding.UTF8.GetBytes(signingString), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
2026-10-03 10:43:03 +02:00
return $"keyId=\"{KeyId}\",algorithm=\"rsa-sha256\",headers=\"{headers}\",signature=\"{signature}\"";
}
public HttpRequest Get(string host, string path)
{
var date = DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture);
var context = new DefaultHttpContext();
context.Request.Method = "GET";
context.Request.Host = new HostString(host);
context.Request.Path = path;
context.Features.Get<IHttpRequestFeature>().RawTarget = path;
context.Request.Headers["Date"] = date;
2026-10-03 10:43:03 +02:00
context.Request.Headers["Signature"] = Signature($"(request-target): get {path}\nhost: {host}\ndate: {date}", "(request-target) host date");
return context.Request;
}
public HttpRequest Post(string host, string path, JsonNode activity)
{
var body = Encoding.UTF8.GetBytes(activity.ToJsonString());
var date = DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture);
var digest = HttpSignatures.Digest(body);
var context = new DefaultHttpContext();
context.Request.Method = "POST";
context.Request.Host = new HostString(host);
context.Request.Path = path;
context.Features.Get<IHttpRequestFeature>().RawTarget = path;
context.Request.Headers["Date"] = date;
context.Request.Headers["Digest"] = digest;
context.Request.Headers["Content-Type"] = "application/activity+json";
2026-10-03 10:43:03 +02:00
context.Request.Headers["Signature"] = Signature($"(request-target): post {path}\nhost: {host}\ndate: {date}\ndigest: {digest}", "(request-target) host date digest");
context.Request.Body = new MemoryStream(body);
context.Request.ContentLength = body.Length;
return context.Request;
}
}
}