public override async Task <bool> Handle() { if (EntityData.IsActivity(MainObject.Data)) { return(true); } var data = MainObject.Data; var createActivity = new ASObject(); createActivity.Type.Add("https://www.w3.org/ns/activitystreams#Create"); createActivity["to"].AddRange(data["to"]); createActivity["bto"].AddRange(data["bto"]); createActivity["cc"].AddRange(data["cc"]); createActivity["bcc"].AddRange(data["bcc"]); createActivity["audience"].AddRange(data["audience"]); createActivity["actor"].Add(ASTerm.MakeId(Actor.Id)); createActivity["object"].Add(ASTerm.MakeId(MainObject.Id)); createActivity.Id = await _urlService.FindUnusedID(EntityStore, createActivity); var activityEntity = await EntityStore.StoreEntity(APEntity.From(createActivity, true)); MainObject = activityEntity; return(true); }
public override async Task <bool> Handle() { if (MainObject.Type != "Delete") { return(true); } var oldObject = await EntityStore.GetEntity((string)MainObject.Data["object"].Single().Primitive, true); var newData = oldObject.Data; foreach (var kv in newData) { if (!DeleteWhitelist.Contains(kv.Key)) { kv.Value.Clear(); } } newData.Replace("type", new ASTerm("Tombstone")); newData.Replace("deleted", new ASTerm(DateTime.Now.ToString("o"))); var newObject = APEntity.From(newData); await EntityStore.StoreEntity(newObject); return(true); }
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))); } }
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 <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)); }
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 Invoke(HttpContext context, IServiceProvider serviceProvider, EntityData entityData, IEntityStore store) { var handler = ActivatorUtilities.CreateInstance <GetEntityHandler>(serviceProvider, context.User); if (entityData.RewriteRequestScheme) { context.Request.Scheme = "https"; } var fullpath = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}"; foreach (var converterFactory in _converters) { if (converterFactory.FileExtension != null && fullpath.EndsWith("." + converterFactory.FileExtension)) { fullpath = fullpath.Substring(0, fullpath.Length - 1 - converterFactory.FileExtension.Length); context.Request.Headers.Remove("Accept"); context.Request.Headers.Add("Accept", converterFactory.RenderMimeType); break; } } /* && ConverterHelpers.GetBestMatch(_converters[0].MimeTypes, context.Request.Headers["Accept"]) != null */ if (context.Request.Method == "OPTIONS") { context.Response.StatusCode = 200; context.Response.Headers.Add("Access-Control-Allow-Credentials", "true"); context.Response.Headers.Add("Access-Control-Allow-Headers", "Authorization"); context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST"); context.Response.Headers.Add("Access-Control-Allow-Origin", context.Request.Headers["Origin"]); context.Response.Headers.Add("Vary", "Origin"); return; } Console.WriteLine(fullpath); foreach (var line in context.Request.Headers["Accept"]) { Console.WriteLine($"---- {line}"); } if (context.Request.Headers["Accept"].Contains("text/event-stream")) { await handler.EventStream(context, fullpath); return; } if (context.WebSockets.IsWebSocketRequest) { await handler.WebSocket(context, fullpath); return; } if (context.Request.Method == "POST" && context.Request.ContentType.StartsWith("multipart/form-data")) { context.Items.Add("fullPath", fullpath); context.Request.Path = "/settings/uploadMedia"; await _next(context); return; } if (context.Request.QueryString.Value == "?hub") { context.Items.Add("fullPath", fullpath); context.Request.Path = "/.well-known/hub"; context.Request.QueryString = QueryString.Empty; await _next(context); return; } IConverter readConverter = null; IConverter writeConverter = null; bool needRead = context.Request.Method == "POST"; var target = fullpath; APEntity targetEntity = null; targetEntity = await store.GetEntity(target, false); if (needRead) { if (targetEntity?.Type == "_inbox") { target = (string)targetEntity.Data["attributedTo"].Single().Primitive; } } if (targetEntity == null) { await _next(context); return; } foreach (var converterFactory in _converters) { bool worksForWrite = converterFactory.CanRender && ConverterHelpers.GetBestMatch(converterFactory.MimeTypes, context.Request.Headers["Accept"]) != null; bool worksForRead = needRead && converterFactory.CanParse && ConverterHelpers.GetBestMatch(converterFactory.MimeTypes, context.Request.ContentType) != null; if (worksForRead && worksForWrite && readConverter == null && writeConverter == null) { readConverter = writeConverter = converterFactory.Build(serviceProvider, target); break; } if (worksForRead && readConverter == null) { readConverter = converterFactory.Build(serviceProvider, target); } if (worksForWrite && writeConverter == null) { writeConverter = converterFactory.Build(serviceProvider, target); } } ASObject data = null; if (readConverter != null) { data = await readConverter.Parse(context.Request.Body); } if (needRead && readConverter != null && writeConverter == null) { writeConverter = readConverter; } if (data == null && needRead && targetEntity != null) { context.Response.StatusCode = 415; await context.Response.WriteAsync("Unknown mime type " + context.Request.ContentType); return; } var arguments = context.Request.Query; try { if (context.Request.Method == "GET" || context.Request.Method == "HEAD" || context.Request.Method == "OPTIONS") { data = await handler.Get(fullpath, arguments, context); } else if (context.Request.Method == "POST" && data != null) { data = await handler.Post(context, fullpath, data); } } catch (UnauthorizedAccessException e) { context.Response.StatusCode = 403; await context.Response.WriteAsync(e.Message); } catch (InvalidOperationException e) { context.Response.StatusCode = 401; await context.Response.WriteAsync(e.Message); } if (context.Response.HasStarted) { return; } if (data != null) { if (context.Request.Method == "HEAD") { context.Response.StatusCode = 200; return; } if (writeConverter != null) { await writeConverter.Render(context.Request, context.Response, data); } else if (context.Request.ContentType == "application/magic-envelope+xml") { context.Response.StatusCode = 202; await context.Response.WriteAsync("accepted"); } else { context.Request.Method = "GET"; context.Request.Path = "/render"; context.Items["object"] = APEntity.From(data); await _next(context); } return; } if (!context.Response.HasStarted) { await _next(context); } }
public async Task <IActionResult> DoBadgeToken([FromBody] BadgeTokenModel model) { var user = await _userManager.FindByNameAsync(model.Username); if (user == null) { user = new APUser { UserName = model.Username, Email = model.Username + "@badge.local" }; await _userManager.CreateAsync(user, model.Password); var uobj = model.Username; var name = model.Username; var obj = new ASObject(); obj["type"].Add(new ASTerm("Person")); obj["preferredUsername"].Add(new ASTerm(name)); obj["name"].Add(new ASTerm(name)); var id = await _entityData.UriFor(_entityStore, obj); obj["id"].Add(new ASTerm(id)); var inbox = await _newCollection("inbox", id); var outbox = await _newCollection("outbox", id); var following = await _newCollection("following", id); var followers = await _newCollection("followers", id); var likes = await _newCollection("likes", id); var blocks = await _newCollection("blocks", id); var blocked = await _newCollection("blocked", id); var blocksData = blocks.Data; blocksData["_blocked"].Add(new ASTerm(blocked.Id)); blocks.Data = blocksData; obj["following"].Add(new ASTerm(following.Id)); obj["followers"].Add(new ASTerm(followers.Id)); obj["blocks"].Add(new ASTerm(blocks.Id)); obj["likes"].Add(new ASTerm(likes.Id)); obj["inbox"].Add(new ASTerm(inbox.Id)); obj["outbox"].Add(new ASTerm(outbox.Id)); var userEntity = await _entityStore.StoreEntity(APEntity.From(obj, true)); await _entityStore.CommitChanges(); _context.UserActorPermissions.Add(new UserActorPermission { UserId = user.Id, ActorId = userEntity.Id, IsAdmin = true }); await _context.SaveChangesAsync(); } var u = await _signInManager.PasswordSignInAsync(model.Username, model.Password, false, false); if (!u.Succeeded) { return(Unauthorized()); } var firstActor = await _context.UserActorPermissions.FirstOrDefaultAsync(a => a.User == user); var claims = new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, user.Id), new Claim(JwtTokenSettings.ActorClaim, firstActor.ActorId) }; var jwt = new JwtSecurityToken( issuer: _tokenSettings.Issuer, audience: _tokenSettings.Audience, claims: claims, notBefore: DateTime.UtcNow, expires: DateTime.UtcNow.Add(TimeSpan.FromDays(7)), signingCredentials: _tokenSettings.Credentials ); var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); return(Json(new BadgeTokenResponse { Actor = firstActor.ActorId, Token = encodedJwt })); }
public override async Task <bool> Handle() { if (MainObject.Type != "https://www.w3.org/ns/activitystreams#Update" && MainObject.Type != "https://www.w3.org/ns/activitystreams#Delete") { return(true); } var activityData = MainObject.Data; var oldObject = await EntityStore.Bypass.GetEntity(activityData["object"].Single().Id, false); if (oldObject == null) { throw new InvalidOperationException("Cannot remove or update a non-existant object!"); } if (!oldObject.IsOwner) { throw new InvalidOperationException("Cannot remove or update an object not on this server!"); } var originatingCreate = (await _relevantEntities.FindRelevantObject("https://www.w3.org/ns/activitystreams#Create", oldObject.Id)).FirstOrDefault(); if (originatingCreate.Data["actor"].Single().Id != Actor.Id) { throw new InvalidOperationException("Cannot remove or update objects that weren't made by you!"); } if (MainObject.Type == "https://www.w3.org/ns/activitystreams#Update") { var newObject = await EntityStore.GetEntity(activityData["object"].Single().Id, false); if (newObject == oldObject) { throw new InvalidOperationException("No new object passed!"); } var data = oldObject.Data; foreach (var item in newObject.Data) { // SequenceEqual ensures that clients doing full-object updates won't cause this exception on e.g. type, attributedTo, etc if (UpdateBlacklist.Contains(item.Key) && (data[item.Key].Count != item.Value.Count || data[item.Key].Zip(item.Value, _isEqual).Any(a => !a))) { throw new InvalidOperationException($"Cannot update key {item.Key} as it's on the blacklist!"); } data[item.Key].Clear(); data[item.Key].AddRange(item.Value); } data.Replace("updated", ASTerm.MakePrimitive(DateTime.Now.ToString("o"))); newObject.Data = data; newObject.Type = oldObject.Type; await EntityStore.StoreEntity(newObject); } else { var newData = oldObject.Data; foreach (var kv in newData) { if (!DeleteWhitelist.Contains(kv.Key)) { kv.Value.Clear(); } } newData.Type.Clear(); newData.Type.Add("https://www.w3.org/ns/activitystreams#Tombstone"); newData.Replace("deleted", ASTerm.MakePrimitive(DateTime.Now.ToString("o"))); var newObject = APEntity.From(newData, true); await EntityStore.StoreEntity(newObject); } return(true); }
public async Task <APEntity> GetEntity(string id, bool doRemote) { if (id == "https://www.w3.org/ns/activitystreams#Public") { var aso = new ASObject(); aso.Type.Add("https://www.w3.org/ns/activitystreams#Collection"); aso.Id = "https://www.w3.org/ns/activitystreams#Public"; var ent = APEntity.From(aso); return(ent); } try { if ((new Uri(id)).Host == "localhost") { return(await Bypass.GetEntity(id, doRemote)); } } catch (UriFormatException) { /* nom */ } APEntity entity = null; if (Bypass != null) { entity = await Bypass.GetEntity(id, doRemote); } if (entity != null && !entity.IsOwner && entity.Data.Type.Any(_collections.Contains) && doRemote) { entity = null; } if (entity?.Type == "_:LazyLoad" && !doRemote) { return(null); } if ((entity != null && entity.Type != "_:LazyLoad") || !doRemote) { return(entity); } var loadUrl = id; if (entity?.Type == "_:LazyLoad") { loadUrl = (string)entity.Data["href"].First().Primitive; } if (loadUrl.StartsWith("tag:")) { return(null); } var htc = new HttpClient(); htc.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "application/activity+json; application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\", application/json, application/atom+xml, text/html"); if (_context != null) { var signatureVerifier = _serviceProvider.GetRequiredService <SignatureVerifier>(); var user = await Bypass.GetEntity(_context.User.FindFirstValue(JwtTokenSettings.ActorClaim), false); if (user != null) { var jwt = await signatureVerifier.BuildJWS(user, id); if (jwt != null) { htc.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", jwt); } } } HttpResponseMessage response = null; try { response = await htc.GetAsync(loadUrl); if (!response.IsSuccessStatusCode) { response = await htc.GetAsync(loadUrl + ".atom"); // hack! if (!response.IsSuccessStatusCode) { return(null); } } } catch (TaskCanceledException) { return(null); // timeout } catch (HttpRequestException) { return(null); } var converters = new List <IConverterFactory> { new AS2ConverterFactory(), new AtomConverterFactory(false) }; tryAgain: ASObject data = null; foreach (var converter in converters) { if (converter.CanParse && ConverterHelpers.GetBestMatch(converter.MimeTypes, response.Content.Headers.ContentType.ToString()) != null) { data = await converter.Build(_serviceProvider, null).Parse(await response.Content.ReadAsStreamAsync()); break; } } if (data == null) { if (response.Headers.Contains("Link")) { var split = string.Join(", ", response.Headers.GetValues("Link")).Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries); foreach (var spl in split) { var args = spl.Split(';').Select(a => a.Trim()).ToArray(); if (args.Contains("rel=\"alternate\"") && args.Contains("type=\"application/atom+xml\"")) { response = await htc.GetAsync(args[0].Substring(1, args[0].Length - 2)); goto tryAgain; } } } try { var links = (await response.Content.ReadAsStringAsync()).Split('\n'); var alt = links.FirstOrDefault(a => a.Contains("application/atom+xml") && a.Contains("alternate")); if (alt == null) { return(null); } var l = XDocument.Parse(alt + "</link>").Root; if (l.Attribute(XNamespace.None + "type")?.Value == "application/atom+xml") { response = await htc.GetAsync(l.Attribute(XNamespace.None + "href")?.Value); goto tryAgain; } } catch (Exception) { } return(null); } // forces out the old lazy load, if used await _entityFlattener.FlattenAndStore(Bypass, data, false); return(await Bypass.GetEntity(id, true)); }
private async Task <ASObject> _unflatten(IEntityStore store, APEntity entity, int depth, IDictionary <string, APEntity> alreadyMapped, bool remote) { if (depth == 0) { return(entity.Data); } var @object = entity.Data; if (_configuration.IsActor(@object) && entity.IsOwner) { @object = _getEndpoints(entity); } var myid = @object.Id; if (myid != null) { alreadyMapped[myid] = entity; } @object["bto"].Clear(); @object["bcc"].Clear(); foreach (var kv in @object) { foreach (var value in kv.Value) { if (value.SubObject != null) { value.SubObject = await _unflatten(store, APEntity.From(value.SubObject), depth - 1, alreadyMapped, remote); } if (value.Id == null) { continue; } if (_mayNotUnflatten.Contains(kv.Key) && (!entity.IsOwner || !UnflattenIfOwner.Contains(kv.Key))) { continue; } var id = value.Id; if (alreadyMapped.ContainsKey(id) && (depth != 3 || kv.Key != "https://www.w3.org/ns/activitystreams#object")) { continue; } var obj = await store.GetEntity(id, false); if (obj == null || _avoidFlatteningTypes.Contains(obj.Type) || obj.Type.StartsWith("_") || (!remote && !obj.IsOwner)) { continue; } value.Primitive = null; value.Id = null; value.SubObject = await _unflatten(store, obj, depth - 1, alreadyMapped, remote); } } return(@object); }
public async Task <APEntity> GetEntity(string id, bool doRemote) { if (id == "https://www.w3.org/ns/activitystreams#Public") { var aso = new ASObject(); aso.Type.Add("https://www.w3.org/ns/activitystreams#Collection"); aso.Id = "https://www.w3.org/ns/activitystreams#Public"; var ent = APEntity.From(aso); return(ent); } string origin = id; try { var uri = new Uri(id); if (uri.Host == "localhost") { return(await Bypass.GetEntity(id, doRemote)); } origin = uri.GetLeftPart(UriPartial.Authority); } catch (UriFormatException) { /* nom */ } APEntity entity = null; if (Bypass != null) { entity = await Bypass.GetEntity(id, doRemote); } /* if (entity == null) * { * var possibilities = (await _getRE().Query(new RelevantEntitiesService.ContainsAnyStatement("https://www.w3.org/ns/activitystreams#url") { id })).Where(a => Uri.IsWellFormedUriString(a.Id, UriKind.Absolute) && (new Uri(a.Id)).GetLeftPart(UriPartial.Authority) == origin).ToList(); * if (possibilities.Count == 1) entity = possibilities.First(); * }*/ if (entity != null && !entity.IsOwner && entity.Data.Type.Any(_collections.Contains) && doRemote) { entity = null; } if (entity != null || !doRemote) { return(entity); } var htc = new HttpClient(); var request = new HttpRequestMessage(HttpMethod.Get, id); request.Headers.TryAddWithoutValidation("Accept", "application/activity+json; application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\", application/json, text/html"); if (_context != null) { var signatureVerifier = _serviceProvider.GetRequiredService <SignatureVerifier>(); var user = await Bypass.GetEntity(_context.User.FindFirstValue("actor"), false); if (user != null) { var jwt = await signatureVerifier.BuildJWS(user, id); if (jwt != null) { request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", jwt); } request.Headers.TryAddWithoutValidation("Signature", await _keyService.BuildHTTPSignature(user.Id, request)); } } HttpResponseMessage response = null; try { response = await htc.SendAsync(request); } catch (TaskCanceledException) { return(null); // timeout } catch (HttpRequestException) { return(null); } ASObject data = null; await response.Content.LoadIntoBufferAsync(); foreach (var converter in ServerConfig.Converters) { if (converter.CanParse && ConverterHelpers.GetBestMatch(converter.MimeTypes, response.Content.Headers.ContentType.ToString()) != null) { try { data = await converter.Build(_serviceProvider, null).Parse(await response.Content.ReadAsStreamAsync()); break; } catch (NullReferenceException e) { Console.WriteLine(e); /* nom */ } } } if (data == null) { return(null); } // forces out the old lazy load, if used await _entityFlattener.FlattenAndStore(Bypass, data, false); return(await Bypass.GetEntity(id, true)); }