diff --git a/src/activitypub/actors.js b/src/activitypub/actors.js index e581801136..93f395e4b4 100644 --- a/src/activitypub/actors.js +++ b/src/activitypub/actors.js @@ -74,13 +74,24 @@ Actors.qualify = async (ids, options = {}) => { ids = ids.filter(uri => uri !== 'loopback' && new URL(uri).host !== nconf.get('url_parsed').host); } + // Separate those who need migration from user to category + const migrate = new Set(); + if (options.qualifyGroup) { + const exists = await db.exists(ids.map(id => `userRemote:${id}`)); + ids.forEach((id, idx) => { + if (exists[idx]) { + migrate.add(id); + } + }); + } + // Only assert those who haven't been seen recently (configurable), unless update flag passed in (force refresh) if (!options.update) { const upperBound = Date.now() - (1000 * 60 * 60 * 24 * meta.config.activitypubUserPruneDays); const lastCrawled = await db.sortedSetScores('usersRemote:lastCrawled', ids.map(id => ((typeof id === 'object' && id.hasOwnProperty('id')) ? id.id : id))); ids = ids.filter((id, idx) => { const timestamp = lastCrawled[idx]; - return !timestamp || timestamp < upperBound; + return migrate.has(id) || !timestamp || timestamp < upperBound; }); } @@ -267,7 +278,10 @@ Actors.assertGroup = async (ids, options = {}) => { * - true: no new IDs processed; all passed-in IDs present. */ - ids = await Actors.qualify(ids, options); + ids = await Actors.qualify(ids, { + qualifyGroup: true, + ...options, + }); if (!ids) { return ids; } diff --git a/test/activitypub/actors.js b/test/activitypub/actors.js index d33524abf8..dd727179d4 100644 --- a/test/activitypub/actors.js +++ b/test/activitypub/actors.js @@ -76,6 +76,20 @@ describe('Actor asserton', () => { assert.strictEqual(assertion[0].cid, actor.id); }); + it('should not migrate a user to a category if .assert is called', async () => { + // ... because the user isn't due for an update and so is filtered out during qualification + const { id } = helpers.mocks.person(); + await activitypub.actors.assert([id]); + + const { actor } = helpers.mocks.group({ id }); + const assertion = await activitypub.actors.assertGroup([id]); + + assert(assertion.length, 0); + + const exists = await user.exists(id); + assert.strictEqual(exists, false); + }); + it('should migrate a user to a category if on re-assertion it identifies as an as:Group', async () => { // This is to handle previous behaviour that saved all as:Group actors as NodeBB users. const { id } = helpers.mocks.person(); @@ -91,7 +105,9 @@ describe('Actor asserton', () => { } const { actor } = helpers.mocks.group({ id }); - const assertion = await activitypub.actors.assert([id], { update: true }); + const assertion = await activitypub.actors.assertGroup([id]); + + assert(assertion && Array.isArray(assertion) && assertion.length === 1); const { topic_count, post_count } = await categories.getCategoryData(id); assert.strictEqual(topic_count, 2);