fix Accept Follow serialization

This commit is contained in:
Vincent Cloutier 2023-03-16 10:23:31 -04:00
parent 8b5d03e0f1
commit 29ba6baddb
8 changed files with 141 additions and 54 deletions

View file

@ -56,10 +56,5 @@ namespace BirdsiteLive.ActivityPub
return null;
}
private class Ac : Activity
{
[JsonPropertyName("object")]
public Activity apObject { get; set; }
}
}
}

View file

@ -5,7 +5,7 @@ namespace BirdsiteLive.ActivityPub
public class Activity
{
[JsonPropertyName("@context")]
public object context { get; set; }
public string context { get; set; }
public string id { get; set; }
public string type { get; set; }
public string actor { get; set; }

View file

@ -5,6 +5,7 @@ namespace BirdsiteLive.ActivityPub
public class NestedActivity
{
[JsonPropertyName("@context")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public object context { get; set; }
public string id { get; set; }
public string type { get; set; }

View file

@ -21,6 +21,8 @@ namespace BirdsiteLive.Domain
Task<HttpStatusCode> PostDataAsync<T>(T data, string targetHost, string actorUrl, string inbox = null);
Task PostNewActivity(ActivityCreateNote note, string username, string noteId, string targetHost,
string targetInbox);
ActivityAcceptFollow BuildAcceptFollow(ActivityFollow activity);
}
public class ActivityPubService : IActivityPubService
@ -73,13 +75,33 @@ namespace BirdsiteLive.Domain
}
}
public async Task<HttpStatusCode> PostDataAsync<T>(T data, string targetHost, string actorUrl, string inbox = null)
public ActivityAcceptFollow BuildAcceptFollow(ActivityFollow activity)
{
var acceptFollow = new ActivityAcceptFollow()
{
context = "https://www.w3.org/ns/activitystreams",
id = $"{activity.apObject}#accepts/follows/{Guid.NewGuid()}",
type = "Accept",
actor = activity.apObject,
apObject = new NestedActivity()
{
id = activity.id,
type = activity.type,
actor = activity.actor,
apObject = activity.apObject
}
};
return acceptFollow;
}
public HttpRequestMessage BuildRequest<T>(T data, string targetHost, string actorUrl,
string inbox = null)
{
var usedInbox = $"/inbox";
if (!string.IsNullOrWhiteSpace(inbox))
usedInbox = inbox;
var json = JsonSerializer.Serialize(data, new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
var json = JsonSerializer.Serialize(data,
new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
var date = DateTime.UtcNow.ToUniversalTime();
var httpDate = date.ToString("r");
@ -88,26 +110,33 @@ namespace BirdsiteLive.Domain
var signature = _cryptoService.SignAndGetSignatureHeader(date, actorUrl, targetHost, digest, usedInbox);
var client = _httpClientFactory.CreateClient();
client.Timeout = TimeSpan.FromSeconds(2);
var httpRequestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri($"https://{targetHost}{usedInbox}"),
Headers =
{
{"Host", targetHost},
{"Date", httpDate},
{"Signature", signature},
{"Digest", $"SHA-256={digest}"}
{ "Host", targetHost },
{ "Date", httpDate },
{ "Signature", signature },
{ "Digest", $"SHA-256={digest}" }
},
Content = new StringContent(json, Encoding.UTF8, "application/ld+json")
};
return httpRequestMessage;
}
public async Task<HttpStatusCode> PostDataAsync<T>(T data, string targetHost, string actorUrl, string inbox = null)
{
var httpRequestMessage = BuildRequest(data, targetHost, actorUrl, inbox);
var client = _httpClientFactory.CreateClient();
client.Timeout = TimeSpan.FromSeconds(2);
var response = await client.SendAsync(httpRequestMessage);
response.EnsureSuccessStatusCode();
_logger.LogInformation("Sent tweet to " + targetHost);
_logger.LogInformation("Tweet content is " + json);
return response.StatusCode;
}

View file

@ -182,20 +182,7 @@ namespace BirdsiteLive.Domain
private async Task<bool> SendAcceptFollowAsync(ActivityFollow activity, string followerHost)
{
var acceptFollow = new ActivityAcceptFollow()
{
context = "https://www.w3.org/ns/activitystreams",
id = $"{activity.apObject}#accepts/follows/{Guid.NewGuid()}",
type = "Accept",
actor = activity.apObject,
apObject = new NestedActivity()
{
id = activity.id,
type = activity.type,
actor = activity.actor,
apObject = activity.apObject
}
};
var acceptFollow = _activityPubService.BuildAcceptFollow(activity);
var result = await _activityPubService.PostDataAsync(acceptFollow, followerHost, activity.apObject);
return result == HttpStatusCode.Accepted ||
result == HttpStatusCode.OK; //TODO: revamp this for better error handling

View file

@ -3,31 +3,21 @@ using Newtonsoft.Json;
namespace BirdsiteLive.ActivityPub.Tests
{
//[TestClass]
//public class ActivityTests
//{
// [TestMethod]
// public void FollowDeserializationTest()
// {
// var json = "{ \"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://mastodon.technology/c94567cf-1fda-42ba-82fc-a0f82f63ccbe\",\"type\":\"Follow\",\"actor\":\"https://mastodon.technology/users/testtest\",\"object\":\"https://4a120ca2680e.ngrok.io/users/manu\"}";
[TestClass]
public class ActivityTests
{
[TestMethod]
public void Serialize()
{
var obj = new Actor
{
type = "Person",
id = "id"
};
// var data = JsonConvert.DeserializeObject<Activity>(json);
var json = JsonConvert.SerializeObject(obj);
// Assert.AreEqual("https://mastodon.technology/c94567cf-1fda-42ba-82fc-a0f82f63ccbe", data.id);
// Assert.AreEqual("Follow", data.type);
// Assert.AreEqual("https://4a120ca2680e.ngrok.io/users/manu", data.apObject);
// }
// [TestMethod]
// public void UndoDeserializationTest()
// {
// var json =
// "{\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://mastodon.technology/users/testtest#follows/225982/undo\",\"type\":\"Undo\",\"actor\":\"https://mastodon.technology/users/testtest\",\"object\":{\"id\":\"https://mastodon.technology/c94567cf-1fda-42ba-82fc-a0f82f63ccbe\",\"type\":\"Follow\",\"actor\":\"https://mastodon.technology/users/testtest\",\"object\":\"https://4a120ca2680e.ngrok.io/users/manu\"}}";
// var data = JsonConvert.DeserializeObject<Activity>(json);
// Assert.AreEqual("https://mastodon.technology/users/testtest#follows/225982/undo", data.id);
// Assert.AreEqual("Undo", data.type);
// Assert.AreEqual("https://4a120ca2680e.ngrok.io/users/manu", data.apObject);
// }
//}
}
}
}

View file

@ -1,6 +1,6 @@
using BirdsiteLive.ActivityPub.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using System.Text.Json;
namespace BirdsiteLive.ActivityPub.Tests
{
@ -28,9 +28,11 @@ namespace BirdsiteLive.ActivityPub.Tests
var data = ApDeserializer.ProcessActivity(json) as ActivityUndoFollow;
Assert.AreEqual("https://mastodon.technology/users/testtest#follows/225982/undo", data.id);
Assert.AreEqual("Undo", data.type);
Assert.AreEqual("https://www.w3.org/ns/activitystreams", data.context);
Assert.AreEqual("Follow", data.apObject.type);
Assert.AreEqual("https://mastodon.technology/users/testtest", data.apObject.actor);
Assert.AreEqual("https://4a120ca2680e.ngrok.io/users/manu", data.apObject.apObject);
Assert.AreEqual(null, data.apObject.context);
}
[TestMethod]
@ -62,6 +64,9 @@ namespace BirdsiteLive.ActivityPub.Tests
Assert.AreEqual("https://mastodon.technology/users/deleteduser", data.actor);
Assert.AreEqual("https://mastodon.technology/users/deleteduser", data.apObject);
}
// {"object":{"object":"https://bird.makeup/users/spectatorindex","id":"https://masto.ai/b89eb86e-c902-48bc-956f-94f081617f18","type":"Follow","actor":"https://masto.ai/users/singha"},"@context":"https://www.w3.org/ns/activitystreams","id":"https://bird.makeup/users/spectatorindex#accepts/follows/27363118-e61e-4710-a41c-75dd5d54912f","type":"Accept","actor":"https://bird.makeup/users/spectatorindex"}
// {"object":{"object":"https://bird.makeup/users/moltke","id":"https://universeodon.com/81cddd78-d7d6-4665-aa21-7bcfbea82b6b","type":"Follow","actor":"https://universeodon.com/users/amhrasmussen"},"@context":"https://www.w3.org/ns/activitystreams","id":"https://bird.makeup/users/moltke#accepts/follows/d28146be-e884-4e91-8385-19fa004f35b3","type":"Accept","actor":"https://bird.makeup/users/moltke"}
//[TestMethod]
//public void NoteDeserializationTest()

View file

@ -0,0 +1,80 @@
using System.Net.Http;
using System.Threading.Tasks;
using BirdsiteLive.ActivityPub;
using BirdsiteLive.Common.Settings;
using BirdsiteLive.Domain.Factories;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace BirdsiteLive.Domain.Tests
{
[TestClass]
public class ActivityServiceTests
{
private readonly InstanceSettings _settings;
#region Ctor
public ActivityServiceTests()
{
_settings = new InstanceSettings
{
Domain = "domain.name"
};
}
#endregion
[TestMethod]
public async Task ActivityTest()
{
var logger1 = new Mock<ILogger<ActivityPubService>>();
var httpFactory = new Mock<IHttpClientFactory>();
var keyFactory = new Mock<MagicKeyFactory>();
var cryptoService = new CryptoService(keyFactory.Object);
httpFactory.Setup(_ => _.CreateClient(string.Empty)).Returns(new HttpClient());
var service = new ActivityPubService(cryptoService, _settings, httpFactory.Object, logger1.Object);
var activity = new ActivityAcceptFollow()
{
id = "awef",
};
var json = "{\"id\":\"awef\"}";
#region Validations
var req = service.BuildRequest(activity, "google.com", "tata", "awef");
Assert.AreEqual(await req.Content.ReadAsStringAsync(), json);
#endregion
}
[TestMethod]
public async Task AcceptFollow()
{
var logger1 = new Mock<ILogger<ActivityPubService>>();
var httpFactory = new Mock<IHttpClientFactory>();
var keyFactory = new Mock<MagicKeyFactory>();
var cryptoService = new CryptoService(keyFactory.Object);
httpFactory.Setup(_ => _.CreateClient(string.Empty)).Returns(new HttpClient());
var service = new ActivityPubService(cryptoService, _settings, httpFactory.Object, logger1.Object);
var json = "{ \"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://mastodon.technology/c94567cf-1fda-42ba-82fc-a0f82f63ccbe\",\"type\":\"Follow\",\"actor\":\"https://mastodon.technology/users/testtest\",\"object\":\"https://4a120ca2680e.ngrok.io/users/manu\"}";
var activity = ApDeserializer.ProcessActivity(json) as ActivityFollow;
var jsonres =
"{\"object\":{\"id\":\"https://mastodon.technology/c94567cf-1fda-42ba-82fc-a0f82f63ccbe\",\"type\":\"Follow\",\"actor\":\"https://mastodon.technology/users/testtest\",\"object\":\"https://4a120ca2680e.ngrok.io/users/manu\"},\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://4a120ca2680e.ngrok.io/users/manu#accepts/follows/32e5fbda-9159-4ede-8249-9d008092d26f\",\"type\":\"Accept\",\"actor\":\"https://4a120ca2680e.ngrok.io/users/manu\"}";
var activityRes = ApDeserializer.ProcessActivity(jsonres) as ActivityAcceptFollow;
#region Validations
var req = service.BuildAcceptFollow(activity);
Assert.AreEqual(req.actor, activityRes.actor);
Assert.AreEqual(req.context, activityRes.context);
#endregion
}
}
}