From a813fbbb88bcfb8b9a61976e2a4fc4aab943fc88 Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Wed, 19 Jun 2024 00:48:59 -0400 Subject: [PATCH] feat: add cookie for site token --- .../backend/src/routers/hosting/puter-site.js | 117 +++++++++++++----- .../backend/src/services/PuterSiteService.js | 16 +-- .../backend/src/services/auth/AuthService.js | 27 +++- 3 files changed, 122 insertions(+), 38 deletions(-) diff --git a/packages/backend/src/routers/hosting/puter-site.js b/packages/backend/src/routers/hosting/puter-site.js index 2129c907c..2e94d85b2 100644 --- a/packages/backend/src/routers/hosting/puter-site.js +++ b/packages/backend/src/routers/hosting/puter-site.js @@ -153,41 +153,100 @@ class PuterSiteMiddleware extends AdvancedBase { if ( site.protected ) { const svc_auth = req.services.get('auth'); - const token = req.query['puter.auth.token']; + + const get_site_actor_from_token = async () => { + const site_token = req.cookies['puter.site.token']; + if ( ! site_token ) return; - acl_config.no_acl = false; - - if ( ! token ) { - const e = APIError.create('token_missing'); - return this.respond_error_({ req, res, e }); - } - - const app_actor = - await svc_auth.authenticate_from_token(token); - - const user_actor = - app_actor.get_related_actor(UserActorType); - - const svc_permission = req.services.get('permission'); - const perm = await (async () => { - if ( user_actor.type.user.id === site.user_id ) { - return {}; + let failed = false; + let site_actor; + try { + site_actor = + await svc_auth.authenticate_from_token(site_token); + } catch (e) { + failed = true; } + + if ( failed ) return; - return await svc_permission.check( - user_actor, `site:uid#${site.uuid}:access` - ); - })(); + if ( ! site_actor ) return; + + // security measure: if 'puter.site.token' is set + // to a different actor type, someone is likely + // trying to exploit the system. + if ( ! (site_actor.type instanceof SiteActorType) ) { + return; + } + + acl_config.actor = site_actor; + + // Refresh the token if it's been 30 seconds since + // the last request + if ( + (Date.now() - site_actor.type.iat*1000) + > + 1000*30 + ) { + const site_token = svc_auth.get_site_app_token({ + site_uid: site.uuid, + }); + res.cookie('puter.site.token', site_token); + } + + return true; + }; - if ( ! perm ) { - const e = APIError.create('forbidden'); - return this.respond_error_({ req, res, e }); + const make_site_actor_from_app_token = async () => { + const token = req.query['puter.auth.token']; + + acl_config.no_acl = false; + + if ( ! token ) { + const e = APIError.create('token_missing'); + return this.respond_error_({ req, res, e }); + } + + const app_actor = + await svc_auth.authenticate_from_token(token); + + const user_actor = + app_actor.get_related_actor(UserActorType); + + const svc_permission = req.services.get('permission'); + const perm = await (async () => { + if ( user_actor.type.user.id === site.user_id ) { + return {}; + } + + return await svc_permission.check( + user_actor, `site:uid#${site.uuid}:access` + ); + })(); + + if ( ! perm ) { + const e = APIError.create('forbidden'); + this.respond_error_({ req, res, e }); + return false; + } + + const site_actor = await Actor.create(SiteActorType, { site }); + acl_config.actor = site_actor; + + // This subdomain is allowed to keep the site actor token, + // so we send it here as a cookie so other html files can + // also load. + const site_token = svc_auth.get_site_app_token({ + site_uid: site.uuid, + }); + res.cookie('puter.site.token', site_token); + return true; } - const site_actor = await Actor.create(SiteActorType, { site }); - acl_config.actor = site_actor; - - console.log('THE SITE ACTOR?', site_actor); + let ok = await get_site_actor_from_token(); + if ( ! ok ) { + ok = await make_site_actor_from_app_token(); + } + if ( ! ok ) return; Object.freeze(acl_config); } diff --git a/packages/backend/src/services/PuterSiteService.js b/packages/backend/src/services/PuterSiteService.js index dd0dfb6be..4100a68a9 100644 --- a/packages/backend/src/services/PuterSiteService.js +++ b/packages/backend/src/services/PuterSiteService.js @@ -103,14 +103,14 @@ class PuterSiteService extends BaseService { return rows[0]; } - // async get_subdomain_by_uid (uid) { - // const rows = await this.db.read( - // `SELECT * FROM subdomains WHERE uuid = ? LIMIT 1`, - // [uid] - // ); - // if ( rows.length === 0 ) return null; - // return rows[0]; - // } + async get_subdomain_by_uid (uid) { + const rows = await this.db.read( + `SELECT * FROM subdomains WHERE uuid = ? LIMIT 1`, + [uid] + ); + if ( rows.length === 0 ) return null; + return rows[0]; + } } module.exports = { diff --git a/packages/backend/src/services/auth/AuthService.js b/packages/backend/src/services/auth/AuthService.js index 07fdde746..ea0dfe451 100644 --- a/packages/backend/src/services/auth/AuthService.js +++ b/packages/backend/src/services/auth/AuthService.js @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -const { Actor, UserActorType, AppUnderUserActorType, AccessTokenActorType } = require("./Actor"); +const { Actor, UserActorType, AppUnderUserActorType, AccessTokenActorType, SiteActorType } = require("./Actor"); const BaseService = require("../BaseService"); const { get_user, get_app } = require("../../helpers"); const { Context } = require("../../util/context"); @@ -130,6 +130,17 @@ class AuthService extends BaseService { type: actor_type, }); } + + if ( decoded.type === 'actor-site' ) { + const site_uid = decoded.site_uid; + const svc_puterSite = this.services.get('puter-site'); + const site = + await svc_puterSite.get_subdomain_by_uid(site_uid); + return Actor.create(SiteActorType, { + site, + iat: decoded.iat, + }); + } throw APIError.create('token_auth_failed'); } @@ -159,6 +170,20 @@ class AuthService extends BaseService { return token; } + + get_site_app_token ({ site_uid }) { + const token = this.modules.jwt.sign( + { + type: 'actor-site', + version: '0.0.0', + site_uid, + }, + this.global_config.jwt_secret, + { expiresIn: '1h' }, + ); + + return token; + } async create_session_ (user, meta = {}) { this.log.info(`CREATING SESSION`);