added achitecture to handle Delete action

This commit is contained in:
Nicolas Constant 2021-12-09 02:02:30 -05:00
parent 5ef8af47eb
commit 93b43ee4a0
No known key found for this signature in database
GPG key ID: 1E9F677FB01A5688
7 changed files with 91 additions and 10 deletions

View file

@ -1,4 +1,5 @@
using System; using System;
using BirdsiteLive.ActivityPub.Models;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace BirdsiteLive.ActivityPub namespace BirdsiteLive.ActivityPub
@ -19,6 +20,8 @@ namespace BirdsiteLive.ActivityPub
if(a.apObject.type == "Follow") if(a.apObject.type == "Follow")
return JsonConvert.DeserializeObject<ActivityUndoFollow>(json); return JsonConvert.DeserializeObject<ActivityUndoFollow>(json);
break; break;
case "Delete":
return JsonConvert.DeserializeObject<ActivityDelete>(json);
case "Accept": case "Accept":
var accept = JsonConvert.DeserializeObject<ActivityAccept>(json); var accept = JsonConvert.DeserializeObject<ActivityAccept>(json);
//var acceptType = JsonConvert.DeserializeObject<Activity>(accept.apObject); //var acceptType = JsonConvert.DeserializeObject<Activity>(accept.apObject);

View file

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace BirdsiteLive.ActivityPub.Models
{
public class ActivityDelete : Activity
{
[JsonProperty("object")]
public object apObject { get; set; }
}
}

View file

@ -7,6 +7,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using BirdsiteLive.ActivityPub; using BirdsiteLive.ActivityPub;
using BirdsiteLive.ActivityPub.Converters; using BirdsiteLive.ActivityPub.Converters;
using BirdsiteLive.ActivityPub.Models;
using BirdsiteLive.Common.Regexes; using BirdsiteLive.Common.Regexes;
using BirdsiteLive.Common.Settings; using BirdsiteLive.Common.Settings;
using BirdsiteLive.Cryptography; using BirdsiteLive.Cryptography;
@ -28,6 +29,7 @@ namespace BirdsiteLive.Domain
Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityUndoFollow activity, string body); Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityUndoFollow activity, string body);
Task<bool> SendRejectFollowAsync(ActivityFollow activity, string followerHost); Task<bool> SendRejectFollowAsync(ActivityFollow activity, string followerHost);
Task<bool> DeleteRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityDelete activity, string body);
} }
public class UserService : IUserService public class UserService : IUserService
@ -258,6 +260,19 @@ namespace BirdsiteLive.Domain
return result == HttpStatusCode.Accepted || result == HttpStatusCode.OK; //TODO: revamp this for better error handling return result == HttpStatusCode.Accepted || result == HttpStatusCode.OK; //TODO: revamp this for better error handling
} }
public async Task<bool> DeleteRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> 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<SignatureValidationResult> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders, string body) private async Task<SignatureValidationResult> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders, string body)
{ {
//Check Date Validity //Check Date Validity

View file

@ -3,6 +3,10 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using BirdsiteLive.ActivityPub;
using BirdsiteLive.ActivityPub.Models;
using BirdsiteLive.Domain;
using BirdsiteLive.Tools;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -13,11 +17,13 @@ namespace BirdsiteLive.Controllers
public class InboxController : ControllerBase public class InboxController : ControllerBase
{ {
private readonly ILogger<InboxController> _logger; private readonly ILogger<InboxController> _logger;
private readonly IUserService _userService;
#region Ctor #region Ctor
public InboxController(ILogger<InboxController> logger) public InboxController(ILogger<InboxController> logger, IUserService userService)
{ {
_logger = logger; _logger = logger;
_userService = userService;
} }
#endregion #endregion
@ -33,6 +39,19 @@ namespace BirdsiteLive.Controllers
_logger.LogTrace("Inbox: {Body}", body); _logger.LogTrace("Inbox: {Body}", body);
//System.IO.File.WriteAllText($@"C:\apdebug\inbox\{Guid.NewGuid()}.json", 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(); return Accepted();

View file

@ -13,6 +13,7 @@ using BirdsiteLive.Common.Regexes;
using BirdsiteLive.Common.Settings; using BirdsiteLive.Common.Settings;
using BirdsiteLive.Domain; using BirdsiteLive.Domain;
using BirdsiteLive.Models; using BirdsiteLive.Models;
using BirdsiteLive.Tools;
using BirdsiteLive.Twitter; using BirdsiteLive.Twitter;
using BirdsiteLive.Twitter.Models; using BirdsiteLive.Twitter.Models;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -142,7 +143,6 @@ namespace BirdsiteLive.Controllers
//System.IO.File.WriteAllText($@"C:\apdebug\{Guid.NewGuid()}.json", body); //System.IO.File.WriteAllText($@"C:\apdebug\{Guid.NewGuid()}.json", body);
var activity = ApDeserializer.ProcessActivity(body); var activity = ApDeserializer.ProcessActivity(body);
// Do something
var signature = r.Headers["Signature"].First(); var signature = r.Headers["Signature"].First();
switch (activity?.type) switch (activity?.type)
@ -150,7 +150,7 @@ namespace BirdsiteLive.Controllers
case "Follow": case "Follow":
{ {
var succeeded = await _userService.FollowRequestedAsync(signature, r.Method, r.Path, 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(); if (succeeded) return Accepted();
else return Unauthorized(); else return Unauthorized();
} }
@ -158,11 +158,18 @@ namespace BirdsiteLive.Controllers
if (activity is ActivityUndoFollow) if (activity is ActivityUndoFollow)
{ {
var succeeded = await _userService.UndoFollowRequestedAsync(signature, r.Method, r.Path, 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(); if (succeeded) return Accepted();
else return Unauthorized(); else return Unauthorized();
} }
return Accepted(); 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: default:
return Accepted(); return Accepted();
} }
@ -184,9 +191,6 @@ namespace BirdsiteLive.Controllers
return Content(jsonApUser, "application/activity+json; charset=utf-8"); return Content(jsonApUser, "application/activity+json; charset=utf-8");
} }
private Dictionary<string, string> RequestHeaders(IHeaderDictionary header)
{
return header.ToDictionary<KeyValuePair<string, StringValues>, string, string>(h => h.Key.ToLowerInvariant(), h => h.Value);
}
} }
} }

View file

@ -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<string, string> RequestHeaders(IHeaderDictionary header)
{
return header.ToDictionary<KeyValuePair<string, StringValues>, string, string>(h => h.Key.ToLowerInvariant(), h => h.Value);
}
}
}

View file

@ -1,4 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using BirdsiteLive.ActivityPub.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace BirdsiteLive.ActivityPub.Tests namespace BirdsiteLive.ActivityPub.Tests
@ -48,6 +49,20 @@ namespace BirdsiteLive.ActivityPub.Tests
Assert.AreEqual("https://mamot.fr/users/testtest", data.apObject.apObject); 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] //[TestMethod]
//public void NoteDeserializationTest() //public void NoteDeserializationTest()
//{ //{