From f22c4a7c32d6904c6af6b913af38caab0c1a9923 Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Wed, 21 Feb 2024 19:04:46 +0100 Subject: [PATCH] [backend/core] Improve error handling in code paths that try to resolve a deleted remote user --- .../ActivityPub/ActivityFetcherService.cs | 3 +++ .../Core/Federation/WebFinger/WebFingerService.cs | 2 +- .../Core/Middleware/AuthorizedFetchMiddleware.cs | 10 ++++++---- .../Core/Middleware/ErrorHandlerMiddleware.cs | 14 ++++++++++---- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityFetcherService.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityFetcherService.cs index f3da15f1..3a65f782 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityFetcherService.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityFetcherService.cs @@ -1,3 +1,4 @@ +using System.Net; using System.Net.Http.Headers; using Iceshrimp.Backend.Core.Database.Tables; using Iceshrimp.Backend.Core.Federation.ActivityStreams; @@ -34,6 +35,8 @@ public class ActivityFetcherService( if (!response.IsSuccessStatusCode) { + if (response.StatusCode == HttpStatusCode.Gone) + throw AuthFetchException.NotFound("The remote user no longer exists."); logger.LogDebug("Failed to fetch activity: response status was {code}", response.StatusCode); return []; } diff --git a/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs b/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs index c3ce163b..a05d4ac6 100644 --- a/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs +++ b/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs @@ -33,7 +33,7 @@ public class WebFingerService(HttpClient client, HttpRequestService httpRqSvc, I var res = await client.SendAsync(req, cts.Token); if (res.StatusCode == HttpStatusCode.Gone) - throw GracefulException.Accepted("The remote user no longer exists."); + throw AuthFetchException.NotFound("The remote user no longer exists."); if (!res.IsSuccessStatusCode) return null; if (res.Content.Headers.ContentType?.MediaType is not "application/jrd+json" and not "application/json") diff --git a/Iceshrimp.Backend/Core/Middleware/AuthorizedFetchMiddleware.cs b/Iceshrimp.Backend/Core/Middleware/AuthorizedFetchMiddleware.cs index aba27046..847aa2f6 100644 --- a/Iceshrimp.Backend/Core/Middleware/AuthorizedFetchMiddleware.cs +++ b/Iceshrimp.Backend/Core/Middleware/AuthorizedFetchMiddleware.cs @@ -61,7 +61,7 @@ public class AuthorizedFetchMiddleware( if (await fedCtrlSvc.ShouldBlockAsync(sig.KeyId)) throw new GracefulException(HttpStatusCode.Forbidden, "Forbidden", "Instance is blocked", - supressLog: true); + suppressLog: true); // First, we check if we already have the key key = await db.UserPublickeys.Include(p => p.User) @@ -93,7 +93,7 @@ public class AuthorizedFetchMiddleware( // We want to check both the user host & the keyId host (as account & web domain might be different) if (await fedCtrlSvc.ShouldBlockAsync(key.User.Host, key.KeyId)) throw new GracefulException(HttpStatusCode.Forbidden, "Forbidden", "Instance is blocked", - supressLog: true); + suppressLog: true); List headers = request.ContentLength > 0 || attribute.ForceBody ? ["(request-target)", "digest", "host", "date"] @@ -112,6 +112,7 @@ public class AuthorizedFetchMiddleware( } catch (Exception e) { + if (e is AuthFetchException afe) throw GracefulException.Accepted(afe.Message); if (e is GracefulException { SuppressLog: true }) throw; logger.LogDebug("Error validating HTTP signature: {error}", e.Message); } @@ -142,7 +143,7 @@ public class AuthorizedFetchMiddleware( throw new Exception("Activity has no actor"); if (await fedCtrlSvc.ShouldBlockAsync(new Uri(activity.Actor.Id).Host)) throw new GracefulException(HttpStatusCode.Forbidden, "Forbidden", "Instance is blocked", - supressLog: true); + suppressLog: true); key = null; key = await db.UserPublickeys .Include(p => p.User) @@ -161,7 +162,7 @@ public class AuthorizedFetchMiddleware( if (await fedCtrlSvc.ShouldBlockAsync(key.User.Host, new Uri(key.KeyId).Host)) throw new GracefulException(HttpStatusCode.Forbidden, "Forbidden", "Instance is blocked", - supressLog: true); + suppressLog: true); // We need to re-run deserialize & expand with date time handling disabled for JSON-LD canonicalization to work correctly var rawDeserialized = JsonConvert.DeserializeObject(body, JsonSerializerSettings); @@ -182,6 +183,7 @@ public class AuthorizedFetchMiddleware( } catch (Exception e) { + if (e is AuthFetchException afe) throw GracefulException.Accepted(afe.Message); if (e is GracefulException { SuppressLog: true }) throw; logger.LogError("Error validating JSON-LD signature: {error}", e.Message); } diff --git a/Iceshrimp.Backend/Core/Middleware/ErrorHandlerMiddleware.cs b/Iceshrimp.Backend/Core/Middleware/ErrorHandlerMiddleware.cs index 576e71d0..d30c92a2 100644 --- a/Iceshrimp.Backend/Core/Middleware/ErrorHandlerMiddleware.cs +++ b/Iceshrimp.Backend/Core/Middleware/ErrorHandlerMiddleware.cs @@ -145,7 +145,7 @@ public class GracefulException( string error, string message, string? details = null, - bool supressLog = false, + bool suppressLog = false, bool overrideBasic = false ) : Exception(message) { @@ -153,7 +153,7 @@ public class GracefulException( public readonly string Error = error; public readonly bool OverrideBasic = overrideBasic; public readonly HttpStatusCode StatusCode = statusCode; - public readonly bool SuppressLog = supressLog; + public readonly bool SuppressLog = suppressLog; public GracefulException(HttpStatusCode statusCode, string message, string? details = null) : this(statusCode, statusCode.ToString(), message, details) { } @@ -190,8 +190,14 @@ public class GracefulException( /// returning 410 Gone) /// public static GracefulException Accepted(string message) => - new(HttpStatusCode.Accepted, HttpStatusCode.Accepted.ToString(), - message, supressLog: true); + new(HttpStatusCode.Accepted, HttpStatusCode.Accepted.ToString(), message, suppressLog: true); +} + +public class AuthFetchException(HttpStatusCode statusCode, string message, string? details = null) + : GracefulException(statusCode, message, details) +{ + public static AuthFetchException NotFound(string message) => + new(HttpStatusCode.NotFound, HttpStatusCode.NotFound.ToString(), message); } public enum ExceptionVerbosity