starting working on activity
This commit is contained in:
parent
e1ef37d504
commit
4875e71340
17 changed files with 329 additions and 5 deletions
30
src/BirdsiteLive.ActivityPub/ApDeserializer.cs
Normal file
30
src/BirdsiteLive.ActivityPub/ApDeserializer.cs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.ActivityPub
|
||||||
|
{
|
||||||
|
public class ApDeserializer
|
||||||
|
{
|
||||||
|
public static Activity ProcessActivity(string json)
|
||||||
|
{
|
||||||
|
var activity = JsonConvert.DeserializeObject<Activity>(json);
|
||||||
|
switch (activity.type)
|
||||||
|
{
|
||||||
|
case "Follow":
|
||||||
|
return JsonConvert.DeserializeObject<ActivityFollow>(json);
|
||||||
|
case "Undo":
|
||||||
|
var a = JsonConvert.DeserializeObject<ActivityUndo>(json);
|
||||||
|
if(a.apObject.type == "Follow")
|
||||||
|
return JsonConvert.DeserializeObject<ActivityUndoFollow>(json);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Ac : Activity
|
||||||
|
{
|
||||||
|
[JsonProperty("object")]
|
||||||
|
public Activity apObject { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||||
<PackageReference Include="System.Text.Json" Version="4.7.2" />
|
<PackageReference Include="System.Text.Json" Version="4.7.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
16
src/BirdsiteLive.ActivityPub/Models/Activity.cs
Normal file
16
src/BirdsiteLive.ActivityPub/Models/Activity.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.ActivityPub
|
||||||
|
{
|
||||||
|
public class Activity
|
||||||
|
{
|
||||||
|
[JsonProperty("@context")]
|
||||||
|
public string context { get; set; }
|
||||||
|
public string id { get; set; }
|
||||||
|
public string type { get; set; }
|
||||||
|
public string actor { get; set; }
|
||||||
|
//[JsonProperty("object")]
|
||||||
|
//public string apObject { get; set; }
|
||||||
|
}
|
||||||
|
}
|
10
src/BirdsiteLive.ActivityPub/Models/ActivityFollow.cs
Normal file
10
src/BirdsiteLive.ActivityPub/Models/ActivityFollow.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.ActivityPub
|
||||||
|
{
|
||||||
|
public class ActivityFollow : Activity
|
||||||
|
{
|
||||||
|
[JsonProperty("object")]
|
||||||
|
public string apObject { get; set; }
|
||||||
|
}
|
||||||
|
}
|
10
src/BirdsiteLive.ActivityPub/Models/ActivityUndo.cs
Normal file
10
src/BirdsiteLive.ActivityPub/Models/ActivityUndo.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.ActivityPub
|
||||||
|
{
|
||||||
|
public class ActivityUndo : Activity
|
||||||
|
{
|
||||||
|
[JsonProperty("object")]
|
||||||
|
public Activity apObject { get; set; }
|
||||||
|
}
|
||||||
|
}
|
10
src/BirdsiteLive.ActivityPub/Models/ActivityUndoFollow.cs
Normal file
10
src/BirdsiteLive.ActivityPub/Models/ActivityUndoFollow.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.ActivityPub
|
||||||
|
{
|
||||||
|
public class ActivityUndoFollow : Activity
|
||||||
|
{
|
||||||
|
[JsonProperty("object")]
|
||||||
|
public ActivityFollow apObject { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace BirdsiteLive.ActivityPub
|
namespace BirdsiteLive.ActivityPub
|
||||||
{
|
{
|
||||||
public class Actor
|
public class Actor
|
||||||
{
|
{
|
||||||
[JsonPropertyName("@context")]
|
//[JsonPropertyName("@context")]
|
||||||
public string[] context { get; set; } = new[] {"https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"};
|
[JsonProperty("@context")]
|
||||||
|
public object[] context { get; set; } = new[] {"https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1" };
|
||||||
public string id { get; set; }
|
public string id { get; set; }
|
||||||
public string type { get; set; }
|
public string type { get; set; }
|
||||||
public string preferredUsername { get; set; }
|
public string preferredUsername { get; set; }
|
26
src/BirdsiteLive.Domain/ActivityPubService.cs
Normal file
26
src/BirdsiteLive.Domain/ActivityPubService.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BirdsiteLive.ActivityPub;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.Domain
|
||||||
|
{
|
||||||
|
public interface IActivityPubService
|
||||||
|
{
|
||||||
|
Task<Actor> GetUser(string objectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ActivityPubService : IActivityPubService
|
||||||
|
{
|
||||||
|
public async Task<Actor> GetUser(string objectId)
|
||||||
|
{
|
||||||
|
using (var httpClient = new HttpClient())
|
||||||
|
{
|
||||||
|
httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||||
|
var result = await httpClient.GetAsync(objectId);
|
||||||
|
var content = await result.Content.ReadAsStringAsync();
|
||||||
|
return JsonConvert.DeserializeObject<Actor>(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,24 +1,33 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using BirdsiteLive.ActivityPub;
|
using BirdsiteLive.ActivityPub;
|
||||||
using BirdsiteLive.Common.Settings;
|
using BirdsiteLive.Common.Settings;
|
||||||
|
using BirdsiteLive.Cryptography;
|
||||||
using BirdsiteLive.Twitter.Models;
|
using BirdsiteLive.Twitter.Models;
|
||||||
|
using Tweetinvi.Core.Exceptions;
|
||||||
|
|
||||||
namespace BirdsiteLive.Domain
|
namespace BirdsiteLive.Domain
|
||||||
{
|
{
|
||||||
public interface IUserService
|
public interface IUserService
|
||||||
{
|
{
|
||||||
Actor GetUser(TwitterUser twitterUser);
|
Actor GetUser(TwitterUser twitterUser);
|
||||||
|
Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UserService : IUserService
|
public class UserService : IUserService
|
||||||
{
|
{
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
|
private readonly IActivityPubService _activityPubService;
|
||||||
private readonly string _host;
|
private readonly string _host;
|
||||||
|
|
||||||
#region Ctor
|
#region Ctor
|
||||||
public UserService(InstanceSettings instanceSettings, ICryptoService cryptoService)
|
public UserService(InstanceSettings instanceSettings, ICryptoService cryptoService, IActivityPubService activityPubService)
|
||||||
{
|
{
|
||||||
_cryptoService = cryptoService;
|
_cryptoService = cryptoService;
|
||||||
|
_activityPubService = activityPubService;
|
||||||
_host = $"https://{instanceSettings.Domain.Replace("https://",string.Empty).Replace("http://", string.Empty).TrimEnd('/')}";
|
_host = $"https://{instanceSettings.Domain.Replace("https://",string.Empty).Replace("http://", string.Empty).TrimEnd('/')}";
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -53,5 +62,78 @@ namespace BirdsiteLive.Domain
|
||||||
};
|
};
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity)
|
||||||
|
{
|
||||||
|
// Validate
|
||||||
|
if (!await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders)) return false;
|
||||||
|
|
||||||
|
// Save Follow in DB
|
||||||
|
|
||||||
|
// Send Accept Activity
|
||||||
|
|
||||||
|
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders)
|
||||||
|
{
|
||||||
|
var signatures = rawSig.Split(',');
|
||||||
|
var signature_header = new Dictionary<string, string>();
|
||||||
|
foreach (var signature in signatures)
|
||||||
|
{
|
||||||
|
var splitSig = signature.Replace("\"", string.Empty).Split('=');
|
||||||
|
signature_header.Add(splitSig[0], splitSig[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
signature_header["signature"] = signature_header["signature"] + "==";
|
||||||
|
|
||||||
|
var key_id = signature_header["keyId"];
|
||||||
|
var headers = signature_header["headers"];
|
||||||
|
var algorithm = signature_header["algorithm"];
|
||||||
|
var sig = Convert.FromBase64String(signature_header["signature"]);
|
||||||
|
|
||||||
|
|
||||||
|
var remoteUser = await _activityPubService.GetUser(actor);
|
||||||
|
|
||||||
|
var toDecode = remoteUser.publicKey.publicKeyPem.Trim().Remove(0, remoteUser.publicKey.publicKeyPem.IndexOf('\n'));
|
||||||
|
toDecode = toDecode.Remove(toDecode.LastIndexOf('\n')).Replace("\n", "");
|
||||||
|
var signKey = ASN1.ToRSA(Convert.FromBase64String(toDecode));
|
||||||
|
|
||||||
|
var toSign = new StringBuilder();
|
||||||
|
//var comparisonString = headers.Split(' ').Select(signed_header_name =>
|
||||||
|
//{
|
||||||
|
// if (signed_header_name == "(request-target)")
|
||||||
|
// return "(request-target): post /inbox";
|
||||||
|
// else
|
||||||
|
// return $"{signed_header_name}: {r.Headers[signed_header_name.ToUpperInvariant()]}";
|
||||||
|
//});
|
||||||
|
|
||||||
|
foreach (var headerKey in headers.Split(' '))
|
||||||
|
{
|
||||||
|
if (headerKey == "(request-target)") toSign.Append($"(request-target): {method.ToLower()} {path}{queryString}\n");
|
||||||
|
else toSign.Append($"{headerKey}: {string.Join(", ", requestHeaders[headerKey])}\n");
|
||||||
|
}
|
||||||
|
toSign.Remove(toSign.Length - 1, 1);
|
||||||
|
|
||||||
|
//var signKey = ASN1.ToRSA(Convert.FromBase64String(toDecode));
|
||||||
|
|
||||||
|
//new RSACryptoServiceProvider(keyId.publicKey.publicKeyPem);
|
||||||
|
|
||||||
|
//Create a new instance of RSACryptoServiceProvider.
|
||||||
|
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
|
||||||
|
|
||||||
|
//Get an instance of RSAParameters from ExportParameters function.
|
||||||
|
RSAParameters RSAKeyInfo = key.ExportParameters(false);
|
||||||
|
|
||||||
|
//Set RSAKeyInfo to the public key values.
|
||||||
|
RSAKeyInfo.Modulus = Convert.FromBase64String(toDecode);
|
||||||
|
|
||||||
|
key.ImportParameters(RSAKeyInfo);
|
||||||
|
|
||||||
|
var result = signKey.VerifyData(Encoding.UTF8.GetBytes(toSign.ToString()), sig, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.Domain", "Bird
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.ActivityPub", "BirdsiteLive.ActivityPub\BirdsiteLive.ActivityPub.csproj", "{7463E1E2-9736-4A46-8507-010BDD8ECFBB}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.ActivityPub", "BirdsiteLive.ActivityPub\BirdsiteLive.ActivityPub.csproj", "{7463E1E2-9736-4A46-8507-010BDD8ECFBB}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.ActivityPub.Tests", "Tests\BirdsiteLive.ActivityPub.Tests\BirdsiteLive.ActivityPub.Tests.csproj", "{1D713961-9926-41FF-8D6A-8A4B8D548484}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -55,6 +57,10 @@ Global
|
||||||
{7463E1E2-9736-4A46-8507-010BDD8ECFBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{7463E1E2-9736-4A46-8507-010BDD8ECFBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{7463E1E2-9736-4A46-8507-010BDD8ECFBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{7463E1E2-9736-4A46-8507-010BDD8ECFBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{7463E1E2-9736-4A46-8507-010BDD8ECFBB}.Release|Any CPU.Build.0 = Release|Any CPU
|
{7463E1E2-9736-4A46-8507-010BDD8ECFBB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -66,6 +72,7 @@ Global
|
||||||
{155D46A4-2D05-47F2-8FFC-0B7C412A7652} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
{155D46A4-2D05-47F2-8FFC-0B7C412A7652} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
||||||
{D48450EE-D8BD-4228-9864-043AC88F7EE0} = {4FEAD6BC-3C8E-451A-8CA1-FF1AF47D26CC}
|
{D48450EE-D8BD-4228-9864-043AC88F7EE0} = {4FEAD6BC-3C8E-451A-8CA1-FF1AF47D26CC}
|
||||||
{7463E1E2-9736-4A46-8507-010BDD8ECFBB} = {4FEAD6BC-3C8E-451A-8CA1-FF1AF47D26CC}
|
{7463E1E2-9736-4A46-8507-010BDD8ECFBB} = {4FEAD6BC-3C8E-451A-8CA1-FF1AF47D26CC}
|
||||||
|
{1D713961-9926-41FF-8D6A-8A4B8D548484} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {69E8DCAD-4C37-4010-858F-5F94E6FBABCE}
|
SolutionGuid = {69E8DCAD-4C37-4010-858F-5F94E6FBABCE}
|
||||||
|
|
|
@ -4,9 +4,13 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BirdsiteLive.ActivityPub;
|
||||||
using BirdsiteLive.Domain;
|
using BirdsiteLive.Domain;
|
||||||
using BirdsiteLive.Twitter;
|
using BirdsiteLive.Twitter;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace BirdsiteLive.Controllers
|
namespace BirdsiteLive.Controllers
|
||||||
{
|
{
|
||||||
|
@ -34,7 +38,8 @@ namespace BirdsiteLive.Controllers
|
||||||
if (r.Contains("application/activity+json"))
|
if (r.Contains("application/activity+json"))
|
||||||
{
|
{
|
||||||
var apUser = _userService.GetUser(user);
|
var apUser = _userService.GetUser(user);
|
||||||
return Json(apUser);
|
var jsonApUser = JsonConvert.SerializeObject(apUser);
|
||||||
|
return Content(jsonApUser, "application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
return View(user);
|
return View(user);
|
||||||
|
@ -48,11 +53,27 @@ namespace BirdsiteLive.Controllers
|
||||||
using (var reader = new StreamReader(Request.Body))
|
using (var reader = new StreamReader(Request.Body))
|
||||||
{
|
{
|
||||||
var body = await reader.ReadToEndAsync();
|
var body = await reader.ReadToEndAsync();
|
||||||
|
var activity = ApDeserializer.ProcessActivity(body);
|
||||||
// Do something
|
// Do something
|
||||||
|
|
||||||
|
switch (activity.type)
|
||||||
|
{
|
||||||
|
case "Follow":
|
||||||
|
var succeeded = await _userService.FollowRequestedAsync(r.Headers["Signature"].First(), r.Method, r.Path, r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow);
|
||||||
|
if (succeeded) return Ok();
|
||||||
|
else return Unauthorized();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Dictionary<string, string> RequestHeaders(IHeaderDictionary header)
|
||||||
|
{
|
||||||
|
return header.ToDictionary<KeyValuePair<string, StringValues>, string, string>(h => h.Key, h => h.Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
33
src/Tests/BirdsiteLive.ActivityPub.Tests/ActivityTests.cs
Normal file
33
src/Tests/BirdsiteLive.ActivityPub.Tests/ActivityTests.cs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
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\"}";
|
||||||
|
|
||||||
|
// var data = JsonConvert.DeserializeObject<Activity>(json);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
21
src/Tests/BirdsiteLive.ActivityPub.Tests/ActorTests.cs
Normal file
21
src/Tests/BirdsiteLive.ActivityPub.Tests/ActorTests.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.ActivityPub.Tests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class ActorTests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void Deserialize()
|
||||||
|
{
|
||||||
|
var json =
|
||||||
|
"{\"@context\":[\"https://www.w3.org/ns/activitystreams\",\"https://w3id.org/security/v1\",{\"manuallyApprovesFollowers\":\"as:manuallyApprovesFollowers\",\"toot\":\"http://joinmastodon.org/ns#\",\"featured\":{\"@id\":\"toot:featured\",\"@type\":\"@id\"},\"alsoKnownAs\":{\"@id\":\"as:alsoKnownAs\",\"@type\":\"@id\"},\"movedTo\":{\"@id\":\"as:movedTo\",\"@type\":\"@id\"},\"schema\":\"http://schema.org#\",\"PropertyValue\":\"schema:PropertyValue\",\"value\":\"schema:value\",\"IdentityProof\":\"toot:IdentityProof\",\"discoverable\":\"toot:discoverable\"}],\"id\":\"https://mastodon.technology/users/testtest\",\"type\":\"Person\",\"following\":\"https://mastodon.technology/users/testtest/following\",\"followers\":\"https://mastodon.technology/users/testtest/followers\",\"inbox\":\"https://mastodon.technology/users/testtest/inbox\",\"outbox\":\"https://mastodon.technology/users/testtest/outbox\",\"featured\":\"https://mastodon.technology/users/testtest/collections/featured\",\"preferredUsername\":\"testtest\",\"name\":\"TESTEST\",\"summary\":\"\u003cp\u003etest \u003cbr /\u003edsqdq65d4sq56d456q4d8zd4q685d45qd4sqd2q1d5zq56d465qsd4q65sd21qsd23q1s5d64qsd8q465d4s5q1d6qsd35qs4dq6sd84q\u003c/p\u003e\",\"url\":\"https://mastodon.technology/@testtest\",\"manuallyApprovesFollowers\":false,\"discoverable\":false,\"publicKey\":{\"id\":\"https://mastodon.technology/users/testtest#main-key\",\"owner\":\"https://mastodon.technology/users/testtest\",\"publicKeyPem\":\"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm7BlbWI/UD/YJj288h/5\nFB0gXZj0BjYVaK28uzTvb4w6eMu4qpbE9NI0bFqrloXzL3z6PaOCL4Myz9uJYolE\nZ9uNVi2OeZmHigNEOT3hkJWzddtrhkg8MLXKPdOETjhVWV3n+na7QWDDIXP7Fuvi\n+osA5LOoqtD1rYs87xUcWQPLCtVHs928FXsCdLO11ofXiNrancSzY17nkuufjWO+\ndLtvz1kx4Mt2V4Fu+DHskQAzPKU2tzGBrtlVQrk+1R63psIuZYDB6e4i7L6/d1Xl\nIQGmBeJfyxiuNIlbfZIbJ3xPYBQaVAnRKtyGVEFMWwZCqMySwc2LBX+rxI20zJ0R\n7wIDAQAB\n-----END PUBLIC KEY-----\n\"},\"tag\":[],\"attachment\":[],\"endpoints\":{\"sharedInbox\":\"https://mastodon.technology/inbox\"}}";
|
||||||
|
|
||||||
|
|
||||||
|
var actor = JsonConvert.DeserializeObject<Actor>(json);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.ActivityPub.Tests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class ApDeserializerTests
|
||||||
|
{
|
||||||
|
[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\"}";
|
||||||
|
|
||||||
|
var data = ApDeserializer.ProcessActivity(json) as ActivityFollow;
|
||||||
|
|
||||||
|
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 = ApDeserializer.ProcessActivity(json) as ActivityUndoFollow;
|
||||||
|
Assert.AreEqual("https://mastodon.technology/users/testtest#follows/225982/undo", data.id);
|
||||||
|
Assert.AreEqual("Undo", data.type);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||||
|
<PackageReference Include="MSTest.TestAdapter" Version="2.1.0" />
|
||||||
|
<PackageReference Include="MSTest.TestFramework" Version="2.1.0" />
|
||||||
|
<PackageReference Include="coverlet.collector" Version="1.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\BirdsiteLive.ActivityPub\BirdsiteLive.ActivityPub.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
Loading…
Add table
Reference in a new issue