diff --git a/src/BirdsiteLive.ActivityPub/ApDeserializer.cs b/src/BirdsiteLive.ActivityPub/ApDeserializer.cs index 17dadbe..169bbe6 100644 --- a/src/BirdsiteLive.ActivityPub/ApDeserializer.cs +++ b/src/BirdsiteLive.ActivityPub/ApDeserializer.cs @@ -1,4 +1,5 @@ using System; +using BirdsiteLive.ActivityPub.Models; using Newtonsoft.Json; namespace BirdsiteLive.ActivityPub @@ -19,6 +20,8 @@ namespace BirdsiteLive.ActivityPub if(a.apObject.type == "Follow") return JsonConvert.DeserializeObject(json); break; + case "Delete": + return JsonConvert.DeserializeObject(json); case "Accept": var accept = JsonConvert.DeserializeObject(json); //var acceptType = JsonConvert.DeserializeObject(accept.apObject); diff --git a/src/BirdsiteLive.ActivityPub/Models/ActivityDelete.cs b/src/BirdsiteLive.ActivityPub/Models/ActivityDelete.cs new file mode 100644 index 0000000..deb7e7f --- /dev/null +++ b/src/BirdsiteLive.ActivityPub/Models/ActivityDelete.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace BirdsiteLive.ActivityPub.Models +{ + public class ActivityDelete : Activity + { + [JsonProperty("object")] + public object apObject { get; set; } + } +} \ No newline at end of file diff --git a/src/BirdsiteLive.Domain/UserService.cs b/src/BirdsiteLive.Domain/UserService.cs index 24c8287..fedfcca 100644 --- a/src/BirdsiteLive.Domain/UserService.cs +++ b/src/BirdsiteLive.Domain/UserService.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading.Tasks; using BirdsiteLive.ActivityPub; using BirdsiteLive.ActivityPub.Converters; +using BirdsiteLive.ActivityPub.Models; using BirdsiteLive.Common.Regexes; using BirdsiteLive.Common.Settings; using BirdsiteLive.Cryptography; @@ -28,6 +29,7 @@ namespace BirdsiteLive.Domain Task UndoFollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary requestHeaders, ActivityUndoFollow activity, string body); Task SendRejectFollowAsync(ActivityFollow activity, string followerHost); + Task DeleteRequestedAsync(string signature, string method, string path, string queryString, Dictionary requestHeaders, ActivityDelete activity, string body); } public class UserService : IUserService @@ -213,7 +215,7 @@ namespace BirdsiteLive.Domain return result == HttpStatusCode.Accepted || result == HttpStatusCode.OK; //TODO: revamp this for better error handling } - + private string OnlyKeepRoute(string inbox, string host) { if (string.IsNullOrWhiteSpace(inbox)) @@ -258,6 +260,19 @@ namespace BirdsiteLive.Domain return result == HttpStatusCode.Accepted || result == HttpStatusCode.OK; //TODO: revamp this for better error handling } + public async Task DeleteRequestedAsync(string signature, string method, string path, string queryString, Dictionary requestHeaders, + ActivityDelete activity, string body) + { + // Validate + var sigValidation = await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders, body); + if (!sigValidation.SignatureIsValidated) return false; + + // Remove user and followings + throw new NotImplementedException(); + + return true; + } + private async Task ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary requestHeaders, string body) { //Check Date Validity diff --git a/src/BirdsiteLive/Controllers/InboxController.cs b/src/BirdsiteLive/Controllers/InboxController.cs index db055cf..f55e22b 100644 --- a/src/BirdsiteLive/Controllers/InboxController.cs +++ b/src/BirdsiteLive/Controllers/InboxController.cs @@ -3,6 +3,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using BirdsiteLive.ActivityPub; +using BirdsiteLive.ActivityPub.Models; +using BirdsiteLive.Domain; +using BirdsiteLive.Tools; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; @@ -13,11 +17,13 @@ namespace BirdsiteLive.Controllers public class InboxController : ControllerBase { private readonly ILogger _logger; + private readonly IUserService _userService; #region Ctor - public InboxController(ILogger logger) + public InboxController(ILogger logger, IUserService userService) { _logger = logger; + _userService = userService; } #endregion @@ -33,6 +39,19 @@ namespace BirdsiteLive.Controllers _logger.LogTrace("Inbox: {Body}", body); //System.IO.File.WriteAllText($@"C:\apdebug\inbox\{Guid.NewGuid()}.json", body); + var activity = ApDeserializer.ProcessActivity(body); + var signature = r.Headers["Signature"].First(); + + switch (activity?.type) + { + case "Delete": + { + var succeeded = await _userService.DeleteRequestedAsync(signature, r.Method, r.Path, + r.QueryString.ToString(), HeaderHandler.RequestHeaders(r.Headers), activity as ActivityDelete, body); + if (succeeded) return Accepted(); + else return Unauthorized(); + } + } } return Accepted(); diff --git a/src/BirdsiteLive/Controllers/UsersController.cs b/src/BirdsiteLive/Controllers/UsersController.cs index 0d5f77b..73be8b0 100644 --- a/src/BirdsiteLive/Controllers/UsersController.cs +++ b/src/BirdsiteLive/Controllers/UsersController.cs @@ -13,6 +13,7 @@ using BirdsiteLive.Common.Regexes; using BirdsiteLive.Common.Settings; using BirdsiteLive.Domain; using BirdsiteLive.Models; +using BirdsiteLive.Tools; using BirdsiteLive.Twitter; using BirdsiteLive.Twitter.Models; using Microsoft.AspNetCore.Http; @@ -142,7 +143,6 @@ namespace BirdsiteLive.Controllers //System.IO.File.WriteAllText($@"C:\apdebug\{Guid.NewGuid()}.json", body); var activity = ApDeserializer.ProcessActivity(body); - // Do something var signature = r.Headers["Signature"].First(); switch (activity?.type) @@ -150,7 +150,7 @@ namespace BirdsiteLive.Controllers case "Follow": { var succeeded = await _userService.FollowRequestedAsync(signature, r.Method, r.Path, - r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow, body); + r.QueryString.ToString(), HeaderHandler.RequestHeaders(r.Headers), activity as ActivityFollow, body); if (succeeded) return Accepted(); else return Unauthorized(); } @@ -158,11 +158,18 @@ namespace BirdsiteLive.Controllers if (activity is ActivityUndoFollow) { var succeeded = await _userService.UndoFollowRequestedAsync(signature, r.Method, r.Path, - r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityUndoFollow, body); + r.QueryString.ToString(), HeaderHandler.RequestHeaders(r.Headers), activity as ActivityUndoFollow, body); if (succeeded) return Accepted(); else return Unauthorized(); } return Accepted(); + case "Delete": + { + var succeeded = await _userService.DeleteRequestedAsync(signature, r.Method, r.Path, + r.QueryString.ToString(), HeaderHandler.RequestHeaders(r.Headers), activity as ActivityDelete, body); + if (succeeded) return Accepted(); + else return Unauthorized(); + } default: return Accepted(); } @@ -184,9 +191,6 @@ namespace BirdsiteLive.Controllers return Content(jsonApUser, "application/activity+json; charset=utf-8"); } - private Dictionary RequestHeaders(IHeaderDictionary header) - { - return header.ToDictionary, string, string>(h => h.Key.ToLowerInvariant(), h => h.Value); - } + } } \ No newline at end of file diff --git a/src/BirdsiteLive/Tools/HeaderHandler.cs b/src/BirdsiteLive/Tools/HeaderHandler.cs new file mode 100644 index 0000000..74ecf29 --- /dev/null +++ b/src/BirdsiteLive/Tools/HeaderHandler.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; + +namespace BirdsiteLive.Tools +{ + public class HeaderHandler + { + public static Dictionary RequestHeaders(IHeaderDictionary header) + { + return header.ToDictionary, string, string>(h => h.Key.ToLowerInvariant(), h => h.Value); + } + } +} \ No newline at end of file diff --git a/src/Tests/BirdsiteLive.ActivityPub.Tests/ApDeserializerTests.cs b/src/Tests/BirdsiteLive.ActivityPub.Tests/ApDeserializerTests.cs index 3c85113..3d64e90 100644 --- a/src/Tests/BirdsiteLive.ActivityPub.Tests/ApDeserializerTests.cs +++ b/src/Tests/BirdsiteLive.ActivityPub.Tests/ApDeserializerTests.cs @@ -1,4 +1,5 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using BirdsiteLive.ActivityPub.Models; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; namespace BirdsiteLive.ActivityPub.Tests @@ -48,6 +49,20 @@ namespace BirdsiteLive.ActivityPub.Tests Assert.AreEqual("https://mamot.fr/users/testtest", data.apObject.apObject); } + [TestMethod] + public void DeleteDeserializationTest() + { + var json = + "{\"@context\": \"https://www.w3.org/ns/activitystreams\", \"id\": \"https://mastodon.technology/users/deleteduser#delete\", \"type\": \"Delete\", \"actor\": \"https://mastodon.technology/users/deleteduser\", \"to\": [\"https://www.w3.org/ns/activitystreams#Public\"],\"object\": \"https://mastodon.technology/users/deleteduser\",\"signature\": {\"type\": \"RsaSignature2017\",\"creator\": \"https://mastodon.technology/users/deleteduser#main-key\",\"created\": \"2020-11-19T22:43:01Z\",\"signatureValue\": \"peksQao4v5N+sMZgHXZ6xZnGaZrd0s+LqZimu63cnp7O5NBJM6gY9AAu/vKUgrh4C50r66f9OQdHg5yChQhc4ViE+yLR/3/e59YQimelmXJPpcC99Nt0YLU/iTRLsBehY3cDdC6+ogJKgpkToQvB6tG2KrPdrkreYh4Il4eXLKMfiQhgdKluOvenLnl2erPWfE02hIu/jpuljyxSuvJunMdU4yQVSZHTtk/I8q3jjzIzhgyb7ICWU5Hkx0H/47Q24ztsvOgiTWNgO+v6l9vA7qIhztENiRPhzGP5RCCzUKRAe6bcSu1Wfa3NKWqB9BeJ7s+2y2bD7ubPbiEE1MQV7Q==\"}}"; + + var data = ApDeserializer.ProcessActivity(json) as ActivityDelete; + + Assert.AreEqual("https://mastodon.technology/users/deleteduser#delete", data.id); + Assert.AreEqual("Delete", data.type); + Assert.AreEqual("https://mastodon.technology/users/deleteduser", data.actor); + Assert.AreEqual("https://mastodon.technology/users/deleteduser", data.apObject); + } + //[TestMethod] //public void NoteDeserializationTest() //{