BirdsiteLive/src/BirdsiteLive.Domain/ActivityPubService.cs

127 lines
4.6 KiB
C#
Raw Normal View History

2020-06-29 03:56:10 +02:00
using System;
2020-08-01 19:19:41 +02:00
using System.Linq;
2020-06-29 03:56:10 +02:00
using System.Net;
using System.Net.Http;
2020-11-21 02:21:44 +01:00
using System.Security.Cryptography;
2020-06-29 03:56:10 +02:00
using System.Text;
2020-06-06 07:29:13 +02:00
using System.Threading.Tasks;
using BirdsiteLive.ActivityPub;
2021-01-10 04:26:17 +01:00
using BirdsiteLive.ActivityPub.Converters;
2020-08-01 04:13:52 +02:00
using BirdsiteLive.ActivityPub.Models;
using BirdsiteLive.Common.Settings;
2021-03-09 01:27:08 +01:00
using Microsoft.Extensions.Logging;
2020-06-06 07:29:13 +02:00
using Newtonsoft.Json;
namespace BirdsiteLive.Domain
{
public interface IActivityPubService
{
Task<Actor> GetUser(string objectId);
2020-06-29 05:42:23 +02:00
Task<HttpStatusCode> PostDataAsync<T>(T data, string targetHost, string actorUrl, string inbox = null);
2021-01-16 06:34:09 +01:00
Task PostNewNoteActivity(Note note, string username, string noteId, string targetHost,
string targetInbox);
2020-06-06 07:29:13 +02:00
}
public class ActivityPubService : IActivityPubService
{
private readonly InstanceSettings _instanceSettings;
2021-01-16 06:51:52 +01:00
private readonly IHttpClientFactory _httpClientFactory;
2020-06-29 03:56:10 +02:00
private readonly ICryptoService _cryptoService;
2021-03-09 01:27:08 +01:00
private readonly ILogger<ActivityPubService> _logger;
2020-06-29 03:56:10 +02:00
#region Ctor
2021-03-09 01:27:08 +01:00
public ActivityPubService(ICryptoService cryptoService, InstanceSettings instanceSettings, IHttpClientFactory httpClientFactory, ILogger<ActivityPubService> logger)
2020-06-29 03:56:10 +02:00
{
_cryptoService = cryptoService;
_instanceSettings = instanceSettings;
2021-01-16 06:51:52 +01:00
_httpClientFactory = httpClientFactory;
2021-03-09 01:27:08 +01:00
_logger = logger;
2020-06-29 03:56:10 +02:00
}
#endregion
2020-06-06 07:29:13 +02:00
public async Task<Actor> GetUser(string objectId)
{
2021-01-16 06:51:52 +01:00
var httpClient = _httpClientFactory.CreateClient();
2021-02-06 06:11:11 +01:00
httpClient.DefaultRequestHeaders.Add("Accept", "application/activity+json");
2021-01-16 06:51:52 +01:00
var result = await httpClient.GetAsync(objectId);
2022-02-09 07:15:48 +01:00
if (result.StatusCode == HttpStatusCode.Gone)
throw new UserIsGoneException();
result.EnsureSuccessStatusCode();
2021-01-16 06:51:52 +01:00
var content = await result.Content.ReadAsStringAsync();
2021-04-13 07:25:36 +02:00
var actor = JsonConvert.DeserializeObject<Actor>(content);
if (string.IsNullOrWhiteSpace(actor.url)) actor.url = objectId;
return actor;
2020-06-06 07:29:13 +02:00
}
2020-06-29 03:56:10 +02:00
2021-01-16 06:34:09 +01:00
public async Task PostNewNoteActivity(Note note, string username, string noteId, string targetHost, string targetInbox)
{
2021-03-09 01:27:08 +01:00
try
{
var actor = UrlFactory.GetActorUrl(_instanceSettings.Domain, username);
var noteUri = UrlFactory.GetNoteUrl(_instanceSettings.Domain, username, noteId);
2021-03-09 01:27:08 +01:00
var now = DateTime.UtcNow;
var nowString = now.ToString("s") + "Z";
2021-01-16 06:51:52 +01:00
2021-03-09 01:27:08 +01:00
var noteActivity = new ActivityCreateNote()
{
context = "https://www.w3.org/ns/activitystreams",
id = $"{noteUri}/activity",
type = "Create",
actor = actor,
published = nowString,
2021-03-09 01:27:08 +01:00
to = note.to,
cc = note.cc,
apObject = note
};
2021-03-09 01:27:08 +01:00
await PostDataAsync(noteActivity, targetHost, actor, targetInbox);
}
catch (Exception e)
{
_logger.LogError(e, "Error sending {Username} post ({NoteId}) to {Host}{Inbox}", username, noteId, targetHost, targetInbox);
throw;
}
}
2020-06-29 05:42:23 +02:00
public async Task<HttpStatusCode> PostDataAsync<T>(T data, string targetHost, string actorUrl, string inbox = null)
2020-06-29 03:56:10 +02:00
{
2020-06-29 05:42:23 +02:00
var usedInbox = $"/inbox";
if (!string.IsNullOrWhiteSpace(inbox))
usedInbox = inbox;
2020-06-29 03:56:10 +02:00
var json = JsonConvert.SerializeObject(data);
var date = DateTime.UtcNow.ToUniversalTime();
var httpDate = date.ToString("r");
2020-06-29 05:42:23 +02:00
2020-12-28 06:43:02 +01:00
var digest = _cryptoService.ComputeSha256Hash(json);
2020-11-21 02:21:44 +01:00
var signature = _cryptoService.SignAndGetSignatureHeader(date, actorUrl, targetHost, digest, usedInbox);
2020-06-29 03:56:10 +02:00
2021-01-16 06:51:52 +01:00
var client = _httpClientFactory.CreateClient();
2020-06-29 03:56:10 +02:00
var httpRequestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
2020-11-21 01:00:31 +01:00
RequestUri = new Uri($"https://{targetHost}{usedInbox}"),
2020-06-29 03:56:10 +02:00
Headers =
{
{"Host", targetHost},
{"Date", httpDate},
2020-11-21 02:21:44 +01:00
{"Signature", signature},
{"Digest", $"SHA-256={digest}"}
2020-06-29 03:56:10 +02:00
},
Content = new StringContent(json, Encoding.UTF8, "application/ld+json")
};
var response = await client.SendAsync(httpRequestMessage);
2021-01-16 06:34:09 +01:00
response.EnsureSuccessStatusCode();
2020-06-29 03:56:10 +02:00
return response.StatusCode;
}
2020-06-06 07:29:13 +02:00
}
}