diff --git a/src/BirdsiteLive.ActivityPub/ApDeserializer.cs b/src/BirdsiteLive.ActivityPub/ApDeserializer.cs index ef978d9..1eaffa8 100644 --- a/src/BirdsiteLive.ActivityPub/ApDeserializer.cs +++ b/src/BirdsiteLive.ActivityPub/ApDeserializer.cs @@ -16,6 +16,31 @@ namespace BirdsiteLive.ActivityPub if(a.apObject.type == "Follow") return JsonConvert.DeserializeObject(json); break; + case "Accept": + var accept = JsonConvert.DeserializeObject(json); + //var acceptType = JsonConvert.DeserializeObject(accept.apObject); + switch ((accept.apObject as dynamic).type.ToString()) + { + case "Follow": + var acceptFollow = new ActivityAcceptFollow() + { + type = accept.type, + id = accept.id, + actor = accept.actor, + context = accept.context, + apObject = new ActivityFollow() + { + id = (accept.apObject as dynamic).id?.ToString(), + type = (accept.apObject as dynamic).type?.ToString(), + actor = (accept.apObject as dynamic).actor?.ToString(), + context = (accept.apObject as dynamic).context?.ToString(), + apObject = (accept.apObject as dynamic).@object?.ToString() + } + }; + return acceptFollow; + break; + } + break; } return null; diff --git a/src/BirdsiteLive.ActivityPub/BirdsiteLive.ActivityPub.csproj b/src/BirdsiteLive.ActivityPub/BirdsiteLive.ActivityPub.csproj index 01a891a..8dfebd7 100644 --- a/src/BirdsiteLive.ActivityPub/BirdsiteLive.ActivityPub.csproj +++ b/src/BirdsiteLive.ActivityPub/BirdsiteLive.ActivityPub.csproj @@ -5,6 +5,7 @@ + diff --git a/src/BirdsiteLive.ActivityPub/Models/Activity.cs b/src/BirdsiteLive.ActivityPub/Models/Activity.cs index 8a93505..0ad51f2 100644 --- a/src/BirdsiteLive.ActivityPub/Models/Activity.cs +++ b/src/BirdsiteLive.ActivityPub/Models/Activity.cs @@ -10,6 +10,7 @@ namespace BirdsiteLive.ActivityPub public string id { get; set; } public string type { get; set; } public string actor { get; set; } + //[JsonProperty("object")] //public string apObject { get; set; } } diff --git a/src/BirdsiteLive.ActivityPub/Models/ActivityAccept.cs b/src/BirdsiteLive.ActivityPub/Models/ActivityAccept.cs new file mode 100644 index 0000000..30b8746 --- /dev/null +++ b/src/BirdsiteLive.ActivityPub/Models/ActivityAccept.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace BirdsiteLive.ActivityPub +{ + public class ActivityAccept : Activity + { + [JsonProperty("object")] + public object apObject { get; set; } + } +} \ No newline at end of file diff --git a/src/BirdsiteLive.ActivityPub/Models/ActivityAcceptFollow.cs b/src/BirdsiteLive.ActivityPub/Models/ActivityAcceptFollow.cs new file mode 100644 index 0000000..f833a43 --- /dev/null +++ b/src/BirdsiteLive.ActivityPub/Models/ActivityAcceptFollow.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace BirdsiteLive.ActivityPub +{ + public class ActivityAcceptFollow : Activity + { + [JsonProperty("object")] + public ActivityFollow apObject { get; set; } + } +} \ No newline at end of file diff --git a/src/BirdsiteLive.Domain/CryptoService.cs b/src/BirdsiteLive.Domain/CryptoService.cs index 151344c..d259306 100644 --- a/src/BirdsiteLive.Domain/CryptoService.cs +++ b/src/BirdsiteLive.Domain/CryptoService.cs @@ -1,10 +1,13 @@ -using BirdsiteLive.Domain.Factories; +using System; +using System.Text; +using BirdsiteLive.Domain.Factories; namespace BirdsiteLive.Domain { public interface ICryptoService { string GetUserPem(string id); + string SignAndGetSignatureHeader(DateTime date, string actor, string host); } public class CryptoService : ICryptoService @@ -22,5 +25,25 @@ namespace BirdsiteLive.Domain { return _magicKeyFactory.GetMagicKey().AsPEM; } + + /// + /// + /// + /// + /// in the form of https://domain.io/actor + /// in the form of domain.io + /// + public string SignAndGetSignatureHeader(DateTime date, string actor, string targethost) + { + var httpDate = date.ToString("r"); + + var signedString = $"(request-target): post /inbox\nhost: {targethost}\ndate: {httpDate}"; + var signedStringBytes = Encoding.UTF8.GetBytes(signedString); + var signature = _magicKeyFactory.GetMagicKey().Sign(signedStringBytes); + var sig64 = Convert.ToBase64String(signature); + + var header = "keyId=\"" + actor + "\",headers=\"(request-target) host date\",signature=\"" + sig64 + "\""; + return header; + } } } \ No newline at end of file diff --git a/src/BirdsiteLive/BirdsiteLive.csproj b/src/BirdsiteLive/BirdsiteLive.csproj index d302ae7..332831e 100644 --- a/src/BirdsiteLive/BirdsiteLive.csproj +++ b/src/BirdsiteLive/BirdsiteLive.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/BirdsiteLive/Controllers/DebugController.cs b/src/BirdsiteLive/Controllers/DebugController.cs new file mode 100644 index 0000000..e8dadad --- /dev/null +++ b/src/BirdsiteLive/Controllers/DebugController.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using BirdsiteLive.ActivityPub; +using BirdsiteLive.Common.Settings; +using BirdsiteLive.Domain; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Newtonsoft.Json; + +namespace BirdsiteLive.Controllers +{ + public class DebugController : Controller + { + private readonly InstanceSettings _instanceSettings; + private readonly ICryptoService _cryptoService; + + #region Ctor + public DebugController(InstanceSettings instanceSettings, ICryptoService cryptoService) + { + _instanceSettings = instanceSettings; + _cryptoService = cryptoService; + } + #endregion + + public IActionResult Index() + { + return View(); + } + + [HttpPost] + public async Task Follow() + { + var actor = $"https://{_instanceSettings.Domain}/users/gra"; + var targethost = "mamot.fr"; + var followActivity = new ActivityFollow() + { + context = "https://www.w3.org/ns/activitystreams", + id = $"https://{_instanceSettings.Domain}/{Guid.NewGuid()}", + type = "Follow", + actor = actor, + apObject = $"https://{targethost}/users/testtest" + }; + + var json = JsonConvert.SerializeObject(followActivity); + + var date = DateTime.UtcNow.ToUniversalTime(); + var httpDate = date.ToString("r"); + var signature = _cryptoService.SignAndGetSignatureHeader(date, actor, targethost); + + var client = new HttpClient(); + var httpRequestMessage = new HttpRequestMessage + { + Method = HttpMethod.Post, + RequestUri = new Uri($"https://{targethost}/inbox"), + Headers = + { + {"Host", targethost}, + {"Date", httpDate}, + {"Signature", signature} + }, + Content = new StringContent(json, Encoding.UTF8, "application/ld+json") + }; + + try + { + var response = await client.SendAsync(httpRequestMessage); + var re = response.ReasonPhrase; + var t = await response.Content.ReadAsStringAsync(); + } + catch (Exception e) + { + throw; + } + + return View("Index"); + } + } + + public static class HtmlHelperExtensions + { + public static bool IsDebug() + { +#if DEBUG + return true; +#else + return false; +#endif + } + } +} \ No newline at end of file diff --git a/src/BirdsiteLive/Controllers/InboxController.cs b/src/BirdsiteLive/Controllers/InboxController.cs index 301bf6e..82de7b3 100644 --- a/src/BirdsiteLive/Controllers/InboxController.cs +++ b/src/BirdsiteLive/Controllers/InboxController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -14,6 +15,15 @@ namespace BirdsiteLive.Controllers [HttpPost] public async Task Inbox() { + var r = Request; + using (var reader = new StreamReader(Request.Body)) + { + var body = await reader.ReadToEndAsync(); + + + } + + throw new NotImplementedException(); } } diff --git a/src/BirdsiteLive/Views/Home/Index.cshtml b/src/BirdsiteLive/Views/Home/Index.cshtml index d2d19bd..6ae1367 100644 --- a/src/BirdsiteLive/Views/Home/Index.cshtml +++ b/src/BirdsiteLive/Views/Home/Index.cshtml @@ -1,8 +1,15 @@ -@{ +@using BirdsiteLive.Controllers; +@{ ViewData["Title"] = "Home Page"; }

Welcome

Learn about building Web apps with ASP.NET Core.

+ + + @if (HtmlHelperExtensions.IsDebug()) + { + Debug + }
diff --git a/src/Tests/BirdsiteLive.ActivityPub.Tests/ApDeserializerTests.cs b/src/Tests/BirdsiteLive.ActivityPub.Tests/ApDeserializerTests.cs index ab4441c..236e2dc 100644 --- a/src/Tests/BirdsiteLive.ActivityPub.Tests/ApDeserializerTests.cs +++ b/src/Tests/BirdsiteLive.ActivityPub.Tests/ApDeserializerTests.cs @@ -31,5 +31,21 @@ namespace BirdsiteLive.ActivityPub.Tests Assert.AreEqual("https://mastodon.technology/users/testtest", data.apObject.actor); Assert.AreEqual("https://4a120ca2680e.ngrok.io/users/manu", data.apObject.apObject); } + + [TestMethod] + public void AcceptDeserializationTest() + { + var json = "{\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://mamot.fr/users/testtest#accepts/follows/333879\",\"type\":\"Accept\",\"actor\":\"https://mamot.fr/users/testtest\",\"object\":{\"id\":\"https://85da1577f778.ngrok.io/f89dfd87-f5ce-4603-83d9-405c0e229989\",\"type\":\"Follow\",\"actor\":\"https://85da1577f778.ngrok.io/users/gra\",\"object\":\"https://mamot.fr/users/testtest\"}}"; + + + var data = ApDeserializer.ProcessActivity(json) as ActivityAcceptFollow; + Assert.AreEqual("https://mamot.fr/users/testtest#accepts/follows/333879", data.id); + Assert.AreEqual("Accept", data.type); + Assert.AreEqual("https://mamot.fr/users/testtest", data.actor); + Assert.AreEqual("https://85da1577f778.ngrok.io/f89dfd87-f5ce-4603-83d9-405c0e229989", data.apObject.id); + Assert.AreEqual("https://85da1577f778.ngrok.io/users/gra", data.apObject.actor); + Assert.AreEqual("Follow", data.apObject.type); + Assert.AreEqual("https://mamot.fr/users/testtest", data.apObject.apObject); + } } } \ No newline at end of file