protected BaseHandler(IEntityStore entityStore, APEntity mainObject, APEntity actor, APEntity targetBox, ClaimsPrincipal user) { EntityStore = entityStore; MainObject = mainObject; Actor = actor; TargetBox = targetBox; User = user; }
private async Task <Account> _processAccount(APEntity entity) { var result = new Account { id = Uri.EscapeDataString(entity.Id), username = (string)entity.Data["preferredUsername"].FirstOrDefault()?.Primitive ?? entity.Id, display_name = (string)entity.Data["name"].FirstOrDefault()?.Primitive ?? entity.Id, locked = entity.Data["manuallyApprovesFollowers"].Any(a => (bool)a.Primitive), created_at = DateTime.Now, note = (string)entity.Data["summary"].FirstOrDefault()?.Primitive ?? "", url = (string)entity.Data["url"].FirstOrDefault()?.Primitive ?? entity.Id, moved = null, followers_count = -1, following_count = -1, statuses_count = -1, avatar = "", avatar_static = "" }; if (entity.IsOwner) { result.acct = result.username; } else { result.acct = result.username + "@" + (new Uri(entity.Id)).Host; } if (entity.Data["icon"].Any()) { result.avatar = result.avatar_static = entity.Data["icon"].First().Id ?? entity.Data["icon"].First().SubObject["url"].First().Id; } var followers = await _entityStore.GetEntity(entity.Data["followers"].First().Id, false); if (followers != null && followers.Data["totalItems"].Any()) { result.followers_count = (int)followers.Data["totalItems"].First().Primitive; } var following = await _entityStore.GetEntity(entity.Data["following"].First().Id, false); if (following != null && following.Data["totalItems"].Any()) { result.following_count = (int)following.Data["totalItems"].First().Primitive; } var outbox = await _entityStore.GetEntity(entity.Data["outbox"].First().Id, false); if (outbox != null && outbox.Data["totalItems"].Any()) { result.statuses_count = (int)outbox.Data["totalItems"].First().Primitive; } return(result); }
public async Task RemoveFromCollection(APEntity collection, string id) { var item = await _context.CollectionItems.FirstOrDefaultAsync(a => a.CollectionId == collection.Id && a.ElementId == id); if (item != null) { _context.CollectionItems.Remove(item); } }
private void _queueInboxDelivery(string targetUrl, APEntity entity) { _context.EventQueue.Add( DeliverToActivityPubTask.Make(new DeliverToActivityPubData { ObjectId = entity.Id, TargetInbox = targetUrl })); }
private void _queueSalmonDelivery(string targetUrl, APEntity entity) { _context.EventQueue.Add( DeliverToSalmonTask.Make(new DeliverToSalmonData { EntityId = entity.Id, SalmonUrl = targetUrl })); }
public async Task QueueDeliveryForEntity(APEntity entity, int collectionId, string ownedBy = null) { var audienceInbox = await _buildAudienceInbox(entity.Data, forward : ownedBy, actor : true); foreach (var target in audienceInbox.Item1) { await _queueInboxDelivery(target, entity); } }
private ASObject _getEndpoints(APEntity entity) { var data = entity.Data; data.Replace("endpoints", new ASTerm(entity.Id + "#endpoints")); data.Replace("publicKey", new ASTerm(entity.Id + "#key")); data.Replace("sharedInbox", new ASTerm(_configuration.BaseUri + "/auth/sharedInbox")); return(data); }
private async Task <APEntity> _flatten(IEntityStore store, ASObject @object, bool generateId, IDictionary <string, APEntity> entities, string parentId = null) { var entity = new APEntity(); if (@object["href"].Any()) // Link { return(null); } if (@object.Id == null) { if (!generateId) { return(null); } @object.Id = await _configuration.FindUnusedID(store, @object, null, parentId); entity.IsOwner = true; } entity.Id = @object.Id; var t = @object.Type.FirstOrDefault(); if (t?.StartsWith("_") != false && t?.StartsWith("_:") != true) { t = "Unknown"; } entity.Type = t; foreach (var kv in @object) { foreach (var value in kv.Value) { if (value.SubObject == null) { continue; } var subObject = await _flatten(store, value.SubObject, generateId, entities, entity.Id); if (subObject == null) { continue; } value.Id = subObject.Id; value.Primitive = null; value.SubObject = null; } } entity.Data = @object; entities[entity.Id] = entity; return(entity); }
private async Task <APEntity> _flatten(IEntityStore store, ASObject @object, bool generateId, IDictionary <string, APEntity> entities, string parentId = null) { var entity = new APEntity(); if (@object["id"].Count == 0) { if (!generateId) { return(null); } @object["id"].Add(new ASTerm(await _configuration.FindUnusedID(store, @object, null, parentId))); entity.IsOwner = true; } entity.Id = (string)@object["id"].First().Primitive; var t = (string)@object["type"].FirstOrDefault()?.Primitive; if (t?.StartsWith("_") != false && t?.StartsWith("_:") != true) { t = "Unknown"; } entity.Type = t; foreach (var kv in @object) { if (!IdHolding.Contains(kv.Key)) { continue; } foreach (var value in kv.Value) { if (value.SubObject == null) { continue; } if (value.SubObject["id"].Any(a => a.Primitive == null)) { continue; // transient object } var subObject = await _flatten(store, value.SubObject, generateId, entities, entity.Id); if (subObject == null) { continue; } value.Primitive = subObject.Id; value.SubObject = null; } } entity.Data = @object; entities[entity.Id] = entity; return(entity); }
private async Task <ASObject> _getCollection(APEntity entity, IQueryCollection arguments) { var from_id = arguments["from_id"].FirstOrDefault(); var collection = entity.Data; bool seePrivate = collection["attributedTo"].Any() && _user.FindFirstValue(JwtTokenSettings.ActorClaim) == (string)collection["attributedTo"].First().Primitive; collection["current"].Add(new ASTerm(entity.Id)); collection["totalItems"].Add(new ASTerm(await _collectionTools.Count(entity.Id))); if (from_id != null) { var fromId = int.Parse(from_id); var items = await _collectionTools.GetItems(entity.Id, fromId, 11); var hasItems = items.Any(); var page = new ASObject(); page["type"].Add(new ASTerm("OrderedCollectionPage")); page["summary"].Add(new ASTerm("A collection")); page["id"].Add(new ASTerm(entity.Id + "?from_id=" + (hasItems ? fromId : 0))); page["partOf"].Add(new ASTerm(entity.Id)); if (collection["attributedTo"].Any()) { page["attributedTo"].Add(collection["attributedTo"].First()); } if (items.Count > 10) { page["next"].Add(new ASTerm(entity.Id + "?from_id=" + (items.Last().CollectionItemId - 1).ToString())); } page["orderedItems"].AddRange(items.Take(10).Select(a => new ASTerm(a.ElementId))); return(page); } else { var items = await _collectionTools.GetItems(entity.Id, count : 10); var hasItems = items.Any(); var page = new ASObject(); page["type"].Add(new ASTerm("OrderedCollectionPage")); page["id"].Add(new ASTerm(entity.Id + "?from_id=" + (hasItems ? items.First().CollectionItemId + 1 : 0))); page["partOf"].Add(new ASTerm(entity.Id)); if (collection["attributedTo"].Any()) { page["attributedTo"].Add(collection["attributedTo"].First()); } if (items.Count > 0) { page["next"].Add(new ASTerm(entity.Id + "?from_id=" + (items.Last().CollectionItemId - 1))); } page["orderedItems"].AddRange(items.Select(a => new ASTerm(a.ElementId))); collection["first"].Add(new ASTerm(page)); } return(collection); }
public async Task <bool> Contains(APEntity collection, string otherId) { var otherEntity = await _getId(otherId); if (otherEntity == null) { return(false); } return(await _connection.ExecuteScalarAsync <bool>("select exists(select 1 from \"CollectionItems\" where \"CollectionId\" = @CollectionId and \"ElementId\" = @ElementId)", new { CollectionId = collection.DbId, ElementId = otherEntity.Value })); }
public async Task RemoveFromCollection(APEntity collection, string id) { var otherEntity = await _getId(id); if (otherEntity == null) { return; } await _connection.ExecuteAsync("delete from \"CollectionItems\" where \"CollectionId\" = @CollectionId and \"ElementId\" = @ElementId", new { CollectionId = collection.DbId, ElementId = otherEntity.Value }); }
public async Task <IActionResult> Proxy(string id) { if (User.FindFirstValue(JwtTokenSettings.ActorClaim) == null) { return(Unauthorized()); } APEntity entity = null; if (id.StartsWith('@')) { id = id.Substring(1); var spl = id.Split(new char[] { '@' }, 2); var host = spl.Length > 1 ? spl[1] : Request.Host.ToString(); var ent = await _relevantEntities.FindEntitiesWithPreferredUsername(spl[0]); var withHost = ent.FirstOrDefault(a => new Uri(a.Id).Host == host); if (withHost == null && spl.Length == 1) { return(NotFound()); } entity = withHost; if (entity == null && spl.Length > 1) { var hc = new HttpClient(); var webfinger = JsonConvert.DeserializeObject <WellKnownController.WebfingerResult>(await hc.GetStringAsync($"https://{host}/.well-known/webfinger?resource=acct:{id}")); var activityStreams = webfinger.links.FirstOrDefault(a => a.rel == "self" && (a.type == "application/activity+json" || a.type == "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")); if (activityStreams == null) { return(NotFound()); } id = activityStreams.href; } } if (entity == null) { entity = await _entityStore.GetEntity(id, true); } if (entity == null) { return(NotFound()); } var unflattened = await _entityFlattener.Unflatten(_entityStore, entity); return(Content(unflattened.Serialize(true).ToString(), "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")); }
public async Task <APEntity> GetEntity(string id, bool doRemote) { string query = null; string parsedId = null; if (id.Contains("?")) { var split = id.Split(new char[] { '?' }, 2); query = split[1]; parsedId = split[0]; } APEntity original; var entity = original = await Bypass.GetEntity(id, false); if (entity == null && parsedId != null) { entity = await Bypass.GetEntity(parsedId, false); } if (entity == null || !entity.IsOwner) { return(doRemote ? await Bypass.GetEntity(id, true) : original); } if (!entity.Type.StartsWith("_") && entity.Type != "OrderedCollection") { return(doRemote ? await Bypass.GetEntity(id, true) : original); } if (query == null) { return(APEntity.From(await _buildCollection(entity), true)); } else { int from_id = int.MaxValue; int to_id = int.MinValue; foreach (var item in query.Split('&')) { var kv = item.Split('='); if (kv[0] == "from_id" && kv.Length > 1) { from_id = int.Parse(kv[1]); } if (kv[0] == "to_id" && kv.Length > 1) { to_id = int.Parse(kv[1]); } } return(APEntity.From(await _buildPage(entity, from_id, to_id))); } }
public async Task <CollectionItem> AddToCollection(APEntity collection, APEntity entity) { var ci = new CollectionItem { Collection = collection, Element = entity, IsPublic = DeliveryService.IsPublic(entity.Data) }; await _context.CollectionItems.AddAsync(ci); return(ci); }
private async Task <ASObject> _getCollection(APEntity entity, IQueryCollection arguments) { var from_id = arguments["from_id"].FirstOrDefault(); var to_id = arguments["to_id"].FirstOrDefault(); var collection = entity.Data; bool seePrivate = collection["attributedTo"].Any() && _user.FindFirstValue("actor") == collection["attributedTo"].First().Id; if (from_id != null || to_id != null) { var fromId = from_id != null?int.Parse(from_id) : int.MaxValue; var toId = to_id != null?int.Parse(to_id) : int.MinValue; var maxitem = (await _collectionTools.GetItems(entity.Id, count: 1)).FirstOrDefault()?.CollectionItemId; var items = await _collectionTools.GetItems(entity.Id, fromId, toId, count : 11); var hasItems = items.Any(); var page = new ASObject(); page.Type.Add("https://www.w3.org/ns/activitystreams#OrderedCollectionPage"); page["summary"].Add(ASTerm.MakePrimitive("A collection")); page.Id = entity.Id + "?from_id=" + (hasItems ? fromId : 0); page["partOf"].Add(ASTerm.MakeId(entity.Id)); if (collection["attributedTo"].Any()) { page["attributedTo"].Add(collection["attributedTo"].First()); } if (items.Count > 0 && items[0].CollectionItemId != maxitem) { page["prev"].Add(ASTerm.MakeId(entity.Id + "?to_id=" + items[0].CollectionItemId.ToString())); } if (items.Count > 10) { page["next"].Add(ASTerm.MakeId(entity.Id + "?from_id=" + (items[9].CollectionItemId - 1).ToString())); } page["orderedItems"].AddRange(items.Take(10).Select(a => ASTerm.MakeId(a.Entity.Id))); return(page); } else { var items = await _collectionTools.GetItems(entity.Id, count : 1); var hasItems = items.Any(); var page = entity.Id + "?from_id=" + (hasItems ? items.First().CollectionItemId + 1 : 0); collection["current"].Add(ASTerm.MakeId(entity.Id)); collection["totalItems"].Add(ASTerm.MakePrimitive(await _collectionTools.Count(entity.Id), ASTerm.NON_NEGATIVE_INTEGER)); collection["first"].Add(ASTerm.MakeId(page)); return(collection); } }
public async Task Render(HttpRequest request, HttpResponse response, APEntity toRender) { response.ContentType = "application/atom+xml"; var user = await _entityStore.GetEntity(toRender.Data["actor"].Single().Id, false); var key = await _keyService.GetKey(user.Id); var magicKey = key != null ? new MagicKey(key.PrivateKey) : MagicKey.Generate(); var doc = await _entryGenerator.Build(toRender.Data); var enveloped = new MagicEnvelope(doc.ToString(), "application/atom+xml", magicKey); await response.WriteAsync(enveloped.Build().ToString()); }
private async Task <APEntity> _newCollection(string type, string attributedTo) { var obj = new ASObject(); obj["type"].Add(new ASTerm("OrderedCollection")); obj["attributedTo"].Add(new ASTerm(attributedTo)); obj.Replace("id", new ASTerm(await _entityData.FindUnusedID(_entityStore, obj, type, attributedTo))); var entity = APEntity.From(obj, true); entity.Type = "_" + type; entity = await _entityStore.StoreEntity(entity); await _entityStore.CommitChanges(); return(entity); }
public async Task Render(HttpRequest request, HttpResponse response, ASObject toRender) { response.ContentType = ConverterHelpers.GetBestMatch(_factory.MimeTypes, request.Headers["Accept"]); if (toRender["type"].Any(a => (string)a.Primitive == "Tombstone")) { response.StatusCode = 410; } response.Headers.Add("Access-Control-Allow-Origin", "*"); var depth = Math.Min(int.Parse(request.Query["depth"].FirstOrDefault() ?? "3"), 5); var unflattened = await _flattener.Unflatten(_entityStore, APEntity.From(toRender), depth); await response.WriteAsync(unflattened.Serialize(true).ToString()); }
public async Task <CollectionItem> AddToCollection(APEntity collection, APEntity entity) { var ci = new CollectionItem { CollectionId = collection.DbId, ElementId = entity.DbId, IsPublic = _isPublic(entity.Data) || EntityData.IsActor(entity.Data) }; await _connection.ExecuteAsync("insert into \"CollectionItems\" (\"CollectionId\", \"ElementId\", \"IsPublic\") values (@CollectionId, @ElementId, @IsPublic)", ci); await _notifier.Notify($"collection/{collection.Id}", entity.Id); return(ci); }
public async Task <APEntity> GetEntity(string id, bool doRemote) { string fragment = null; string parsedId = null; // no fragment, so who cares if (!id.Contains("#")) { return(await Bypass.GetEntity(id, doRemote)); } var split = id.Split(new char[] { '#' }, 2); fragment = split[1]; parsedId = split[0]; // try local get var entity = await Bypass.GetEntity(id, false); if (entity != null) { return(entity); } // doesn't exist, so try non-fragment entity = await Bypass.GetEntity(parsedId, false); if (entity != null && entity.IsOwner) { // exists, and I own it! var result = await _fakeEntityService.BuildFakeObject(entity, fragment); if (result == null) { return(null); } return(APEntity.From(result, true)); } if (!doRemote) { return(null); } return(await Bypass.GetEntity(id, true)); }
private async Task <ASObject> _buildCollection(APEntity entity) { var collection = entity.Data; collection["current"].Add(ASTerm.MakeId(entity.Id)); collection["totalItems"].Add(ASTerm.MakePrimitive(await _collectionTools.Count(entity.Id), ASTerm.NON_NEGATIVE_INTEGER)); var item = await _collectionTools.GetItems(entity.Id, count : 1); if (item.Any()) { collection["first"].Add(ASTerm.MakeId(entity.Id + $"?from_id={item.First().CollectionItemId + 1}")); } else { collection["first"].Add(ASTerm.MakeId(entity.Id + $"?from_id=0")); } return(collection); }
public async Task <ASObject> BuildFakeObject(APEntity entity, string fragment) { if (!EntityData.IsActor(entity.Data)) { return(null); } if (fragment == "key") { var key = await _keyService.GetKey(entity.Id); var salm = new MagicKey(key.PrivateKey); var pemData = salm.AsPEM; var keyObj = new ASObject(); keyObj.Replace("owner", ASTerm.MakeId(entity.Id)); keyObj.Replace("publicKeyPem", ASTerm.MakePrimitive(pemData)); keyObj.Id = $"{entity.Id}#key"; return(keyObj); } else if (fragment == "endpoints") { var data = entity.Data; var idu = new Uri(entity.Id); var basePath = $"{idu.Scheme}://{idu.Host}{(idu.IsDefaultPort?"":$":{idu.Port}")}{_configuration.BasePath}"; var endpoints = new ASObject(); endpoints.Replace("oauthAuthorizationEndpoint", ASTerm.MakeId(basePath + "oauth/authorize?id=" + Uri.EscapeDataString(entity.Id))); endpoints.Replace("oauthTokenEndpoint", ASTerm.MakeId(basePath + "oauth/token?")); endpoints.Replace("settingsEndpoint", ASTerm.MakeId(basePath + "settings/auth")); endpoints.Replace("uploadMedia", data["outbox"].Single()); endpoints.Replace("relevantObjects", ASTerm.MakeId(basePath + "settings/relevant")); endpoints.Replace("proxyUrl", ASTerm.MakeId(basePath + "auth/proxy")); endpoints.Replace("jwks", ASTerm.MakeId(basePath + "auth/jwks?id=" + Uri.EscapeDataString(entity.Id))); endpoints.Replace("sharedInbox", ASTerm.MakeId(basePath + "auth/sharedInbox")); endpoints.Replace("search", ASTerm.MakeId(basePath + "auth/search")); endpoints.Id = entity.Id + "#endpoints"; return(endpoints); } return(null); }
public async Task <APEntity> StoreEntity(APEntity entity) { var exists = await _context.Entities.FirstOrDefaultAsync(a => a.Id == entity.Id); if (exists == null) { entity.Updated = DateTime.Now; await _context.Entities.AddAsync(entity); } else { exists.SerializedData = entity.SerializedData; exists.Updated = DateTime.Now; exists.Type = entity.Type; entity = exists; } return(entity); }
public async Task Render(HttpRequest request, HttpResponse response, APEntity toRender) { response.ContentType = ConverterHelpers.GetBestMatch(_factory.MimeTypes, request.Headers["Accept"]); if (toRender.Type.Contains("Tombstone")) { response.StatusCode = 410; } if (request.Method == "POST") { response.StatusCode = 201; response.Headers.Add("Location", toRender.Id); } var depth = Math.Min(int.Parse(request.Query["depth"].FirstOrDefault() ?? "3"), 5); var unflattened = await _flattener.Unflatten(_entityStore, toRender, depth, isOwner : toRender.IsOwner); await response.WriteAsync(unflattened.Serialize(true).ToString()); }
public async Task QueueDeliveryForEntity(APEntity entity, int collectionId, string ownedBy = null) { var audienceInbox = await _buildAudienceInbox(entity.Data, forward : ownedBy, actor : true); // Is public post? if (audienceInbox.Item2 && ownedBy == null) { await _queueWebsubDelivery(entity.Data["actor"].First().Id, collectionId, entity.Id); } foreach (var target in audienceInbox.Item1) { await _queueInboxDelivery(target, entity); } foreach (var salmon in audienceInbox.Item3) { await _queueSalmonDelivery(salmon, entity); } }
public async Task <IActionResult> RelevantEntities(RelevantObjectsModel model) { var user = User.FindFirstValue(JwtTokenSettings.ActorClaim); if (user == null) { return(Json(new List <ASObject>())); } var relevant = await _relevantEntities.FindRelevantObject(user, null, model.id); ASObject relevantObject = new ASObject(); foreach (var item in relevant) { relevantObject["relevant"].Add(ASTerm.MakeSubObject(item.Data)); } return(Content((await _flattener.Unflatten(_entityStore, APEntity.From(relevantObject), 5)).Serialize().ToString(), "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")); }
public async Task <APEntity> Get(string url, IQueryCollection arguments, HttpContext context, APEntity existing) { var userId = _user.FindFirstValue("actor"); var entity = existing ?? await _mainStore.GetEntity(url, false); if (entity == null) { return(null); } if (userId == null) { userId = await _verifier.Verify(url, context); } if (!await _authorizer.VerifyAccess(entity, userId)) { var unauth = new ASObject(); unauth.Id = "kroeg:unauthorized"; unauth.Type.Add("kroeg:Unauthorized"); return(APEntity.From(unauth)); } if (entity.Type == "https://www.w3.org/ns/activitystreams#OrderedCollection" || entity.Type == "https://www.w3.org/ns/activitystreams#Collection" || entity.Type.StartsWith("_")) { if (entity.IsOwner && !entity.Data["totalItems"].Any()) { try { return(APEntity.From(await _getCollection(entity, arguments), true)); } catch (FormatException) { throw new InvalidOperationException("Invalid parameters!"); } } else { return((await _mainStore.GetEntity(url + context.Request.QueryString.Value, true)) ?? entity); } } return(entity); }
public async Task <ASObject> Unflatten(IEntityStore store, APEntity entity, int depth = 3, Dictionary <string, APEntity> mapped = null) { if (mapped == null) { mapped = new Dictionary <string, APEntity>(); } if (entity.Id != null) { var e = await store.GetEntity(entity.Id, false); if (e?.IsOwner == true) { entity.IsOwner = true; } } var unflattened = await _unflatten(store, entity, depth, mapped, _configuration.UnflattenRemotely); return(unflattened); }
private ASObject _getEndpoints(APEntity entity) { var data = entity.Data; var idu = new Uri(entity.Id); var basePath = $"{idu.Scheme}://{idu.Host}{_configuration.BasePath}"; var endpoints = new ASObject(); endpoints.Replace("oauthAuthorizationEndpoint", new ASTerm(basePath + "auth/oauth?id=" + Uri.EscapeDataString(entity.Id))); endpoints.Replace("oauthTokenEndpoint", new ASTerm(basePath + "auth/token?")); endpoints.Replace("settingsEndpoint", new ASTerm(basePath + "settings/auth")); endpoints.Replace("uploadMedia", new ASTerm((string)data["outbox"].Single().Primitive)); endpoints.Replace("relevantObjects", new ASTerm(basePath + "settings/relevant")); endpoints.Replace("proxyUrl", new ASTerm(basePath + "auth/proxy")); endpoints.Replace("jwks", new ASTerm(basePath + "auth/jwks?id=" + Uri.EscapeDataString(entity.Id))); endpoints.Replace("id", new ASTerm((string)null)); data.Replace("endpoints", new ASTerm(endpoints)); data.Replace("publicKey", new ASTerm(basePath + "auth/key?id=" + Uri.EscapeDataString(entity.Id))); return(data); }