From 4d71e429a0d0890184635727e227759876347fed Mon Sep 17 00:00:00 2001 From: Justin Wind Date: Thu, 3 Nov 2022 13:09:32 -0700 Subject: [PATCH] consider path when determining if an IA profile user can view a topic --- CHANGELOG.md | 1 + src/manager.js | 19 +++++++++++++++++-- test/src/manager.js | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 810434f..cdf9b9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Releases and notable changes to this project are documented here. ### Fixed +- Path prefix is now considered when determining whether an IndieAuth profile user can view a topic. - Fixed non-default topic lease durations issues with postgres. - Dependency updates. diff --git a/src/manager.js b/src/manager.js index 9d4a282..b6d2ccb 100644 --- a/src/manager.js +++ b/src/manager.js @@ -606,6 +606,21 @@ class Manager { this.logger.info(_scope, 'finished', { ctx }); } + + /** + * Determine if a profile url matches enough of a topic url to describe control over it. + * Topic must match hostname and start with the profile's path. + * @param {URL} profileUrlObj + * @param {URL} topicUrlObj + * @returns {Boolean} + */ + static _profileControlsTopic(profileUrlObj, topicUrlObj) { + const hostnameMatches = profileUrlObj.hostname === topicUrlObj.hostname; + const pathIsPrefix = topicUrlObj.pathname.startsWith(profileUrlObj.pathname); + return hostnameMatches && pathIsPrefix; + } + + /** * GET request for authorized /admin information. * @param {http.ServerResponse} res @@ -625,7 +640,7 @@ class Manager { const profileUrlObj = new URL(ctx.session.authenticatedProfile); ctx.topics = ctx.topics.filter((topic) => { const topicUrlObj = new URL(topic.url); - return (topicUrlObj.hostname === profileUrlObj.hostname); + return Manager._profileControlsTopic(profileUrlObj, topicUrlObj); }); } @@ -659,7 +674,7 @@ class Manager { if (ctx.session && ctx.session.authenticatedProfile) { const profileUrlObj = new URL(ctx.session.authenticatedProfile); const topicUrlObj = new URL(ctx.topic.url); - if (topicUrlObj.hostname !== profileUrlObj.hostname) { + if (!Manager._profileControlsTopic(profileUrlObj, topicUrlObj)) { ctx.topic = null; ctx.subscriptions = []; } diff --git a/test/src/manager.js b/test/src/manager.js index cbd8324..9d8c380 100644 --- a/test/src/manager.js +++ b/test/src/manager.js @@ -207,7 +207,7 @@ describe('Manager', function () { manager.db.topicGetById.resolves({ id: '56c557ce-e667-11eb-bd80-0025905f714a', created: new Date(), - url: 'https://example.com/', + url: 'https://example.com/topic', leaseSecondsPreferred: 123, leaseSecondsMin: 12, leaseSecondsMax: 123456789, @@ -254,7 +254,7 @@ describe('Manager', function () { }); it('covers matching profile', async function () { ctx.session = { - authenticatedProfile: 'https://example.com/profile', + authenticatedProfile: 'https://example.com/', }; await manager.getTopicDetails(res, ctx); assert(ctx.topic); @@ -367,6 +367,34 @@ describe('Manager', function () { }); }); // postRoot + describe('_profileControlsTopic', function () { + let profileUrlObj, topicUrlObj; + it('allows exact match', function () { + profileUrlObj = new URL('https://profile.example.com/'); + topicUrlObj = new URL('https://profile.example.com/'); + const result = Manager._profileControlsTopic(profileUrlObj, topicUrlObj); + assert.strictEqual(result, true); + }); + it('allows descendent-path match', function () { + profileUrlObj = new URL('https://profile.example.com/'); + topicUrlObj = new URL('https://profile.example.com/feed/atom'); + const result = Manager._profileControlsTopic(profileUrlObj, topicUrlObj); + assert.strictEqual(result, true); + }); + it('disallows non-descendent-path', function () { + profileUrlObj = new URL('https://profile.example.com/itsame'); + topicUrlObj = new URL('https://profile.example.com/'); + const result = Manager._profileControlsTopic(profileUrlObj, topicUrlObj); + assert.strictEqual(result, false); + }); + it('disallows non-matched host', function () { + profileUrlObj = new URL('https://profile.example.com/itsame'); + topicUrlObj = new URL('https://elsewhere.example.com/itsame/feed'); + const result = Manager._profileControlsTopic(profileUrlObj, topicUrlObj); + assert.strictEqual(result, false); + }); + }); // _profileControlsTopic + describe('_getRootData', function () { it('extracts expected values', function () { req.getHeader.returns('user@example.com'); -- 2.45.2