Fix federation issues caused by Delete actor activities

This commit is contained in:
Shadowfacts 2019-06-30 14:59:39 -04:00
parent b44a37de5a
commit 4743d69f95
Signed by untrusted user: shadowfacts
GPG Key ID: 94A5AB95422746E5
3 changed files with 25 additions and 8 deletions

View File

@ -24,7 +24,7 @@ function createActivity(article: Article): Create {
return createObject; return createObject;
} }
export async function getActor(url: string, db: Database, forceUpdate: boolean = false): Promise<Actor> { export async function getActor(url: string, db: Database, forceUpdate: boolean = false): Promise<Actor | null> {
if (!forceUpdate) { if (!forceUpdate) {
try { try {
const cached = await getCachedActor(url, db); const cached = await getCachedActor(url, db);
@ -34,7 +34,7 @@ export async function getActor(url: string, db: Database, forceUpdate: boolean =
} }
} }
const remote = await fetchActor(url); const remote = await fetchActor(url);
cacheActor(remote, db); if (remote) cacheActor(remote, db);
return remote; return remote;
} }
@ -80,7 +80,7 @@ async function cacheActor(actor: Actor, db: Database) {
}); });
} }
async function fetchActor(url: string): Promise<Actor> { async function fetchActor(url: string): Promise<Actor | null> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request({ request({
url, url,
@ -91,7 +91,7 @@ async function fetchActor(url: string): Promise<Actor> {
json: true json: true
}, (err, res) => { }, (err, res) => {
if (err) reject(err); if (err) reject(err);
else resolve(res.body as Actor); else resolve(res.body ? res.body as Actor : null);
}); });
}); });
} }

View File

@ -41,6 +41,11 @@ async function handleFollow(activity: Activity, req: Request, res: Response) {
} }
const db = req.app.get("db") as Database; const db = req.app.get("db") as Database;
const actor = await getActor(follow.actor, db, true); // always force re-fetch the actor on follow const actor = await getActor(follow.actor, db, true); // always force re-fetch the actor on follow
if (!actor) {
// if the actor ceases existing between the time the Follow is sent and when receive it, ignore it and end the request
res.end();
return;
}
const acceptObject = <Accept>{ const acceptObject = <Accept>{
"@context": [ "@context": [
"https://www.w3.org/ns/activitystreams", "https://www.w3.org/ns/activitystreams",

View File

@ -11,16 +11,28 @@ export = async (req: Request, res: Response, next: NextFunction) => {
} }
const db = req.app.get("db") as Database; const db = req.app.get("db") as Database;
const actor = await getActor(req.body.actor as string, db); const actor = await getActor(req.body.actor as string, db);
if (validate(req, actor.publicKey.publicKeyPem)) { if (actor && validate(req, actor.publicKey.publicKeyPem)) {
next(); next();
} else { } else {
// if the first check fails, force re-fetch the actor and try again // if the first check fails, force re-fetch the actor and try again
const actor = await getActor(req.body.actor as string, db, true); const actor = await getActor(req.body.actor as string, db, true);
if (validate(req, actor.publicKey.publicKeyPem)) { if (!actor) {
next(); // probably caused by Delete activity for an actor
} else { if (req.body.type === "Delete") {
// if we don't have a cached copy of the key, we have had no interaction with actor
// so the Delete can be safely ignored
// we still send a 200 OK status, so that the originating instances knows it has successfully
// delivered the Delete (we just can't act on it)
res.status(200).end();
} else {
console.log(`Could not retrieve actor ${req.body.actor} to validate HTTP signature for`, req.body);
res.status(401).end("Could not retrieve actor to validate HTTP signature");
}
} else if (!validate(req, actor.publicKey.publicKeyPem)) {
console.log(`Could not validate HTTP signature for ${req.body.actor}`); console.log(`Could not validate HTTP signature for ${req.body.actor}`);
res.status(401).end("Could not validate HTTP signature"); res.status(401).end("Could not validate HTTP signature");
} else {
next();
} }
} }
}; };