diff --git a/core/activitypub_actor.js b/core/activitypub_actor.js index dd91525e..fa7a65fd 100644 --- a/core/activitypub_actor.js +++ b/core/activitypub_actor.js @@ -4,9 +4,15 @@ // ENiGMA½ const actorDb = require('./database.js').dbs.actor; const { Errors } = require('./enig_error.js'); -const Events = require('./events.js'); const { ActorProps } = require('./activitypub_actor_property'); -const { isValidLink } = require('./activitypub_util'); +const UserProps = require('./user_property'); +const { + webFingerProfileUrl, + makeUserUrl, + selfUrl, + isValidLink, +} = require('./activitypub_util'); +const Log = require('./logger').log; // deps const assert = require('assert'); @@ -54,8 +60,55 @@ module.exports = class Actor { return true; } - // :TODO: create an Actor object from a local user - static fromLocalUser(userId, cb) {} + // :TODO: from a User object + static fromLocalUser(user, webServer, cb) { + const userSelfUrl = selfUrl(webServer, user); + + const obj = { + '@context': [ + 'https://www.w3.org/ns/activitystreams', + 'https://w3id.org/security/v1', // :TODO: add support + ], + id: userSelfUrl, + type: 'Person', + preferredUsername: user.username, + name: user.getSanitizedName('real'), + endpoints: { + sharedInbox: 'TODO', + }, + inbox: makeUserUrl(webServer, user, '/ap/users/') + '/inbox', + outbox: makeUserUrl(webServer, user, '/ap/users/') + '/outbox', + followers: makeUserUrl(webServer, user, '/ap/users/') + '/followers', + following: makeUserUrl(webServer, user, '/ap/users/') + '/following', + summary: user.getProperty(UserProps.AutoSignature) || '', + url: webFingerProfileUrl(webServer, user), + + // :TODO: we can start to define BBS related stuff with the community perhaps + // attachment: [ + // { + // name: 'SomeNetwork Address', + // type: 'PropertyValue', + // value: 'Mateo@21:1/121', + // }, + // ], + }; + + const publicKeyPem = user.getProperty(UserProps.PublicKeyMain); + if (!_.isEmpty(publicKeyPem)) { + obj.publicKey = { + id: userSelfUrl + '#main-key', + owner: userSelfUrl, + publicKeyPem, + }; + } else { + Log.warn( + { username: user.username }, + `No public key (${UserProps.PublicKeyMain}) for user "${user.username}"` + ); + } + + return cb(null, new Actor(obj)); + } static fromRemoteUrl(url, cb) { const headers = { diff --git a/core/servers/content/web_handlers/activitypub.js b/core/servers/content/web_handlers/activitypub.js index f017e683..968d3678 100644 --- a/core/servers/content/web_handlers/activitypub.js +++ b/core/servers/content/web_handlers/activitypub.js @@ -214,54 +214,6 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule { }); } - // :TODO: replace me with a fetch-and-cache in Actor, wrapped in e.g. Actor.fetch(url, options, cb) - _fetchActor(actorUrl, cb) { - const headers = { - Accept: 'application/activity+json', - }; - https - .get(actorUrl, { headers }, res => { - if (res.statusCode !== 200) { - return cb(Errors.Invalid(`Bad HTTP status code: ${req.statusCode}`)); - } - - const contentType = res.headers['content-type']; - if ( - !_.isString(contentType) || - !contentType.startsWith('application/activity+json') - ) { - return cb(Errors.Invalid(`Invalid Content-Type: ${contentType}`)); - } - - res.setEncoding('utf8'); - let body = ''; - res.on('data', data => { - body += data; - }); - - res.on('end', () => { - try { - const actor = JSON.parse(body); - if ( - !Array.isArray(actor['@context']) || - actor['@context'][0] !== - 'https://www.w3.org/ns/activitystreams' - ) { - return cb( - Errors.Invalid('Invalid or missing Actor "@context"') - ); - } - return cb(null, actor); - } catch (e) { - return cb(e); - } - }); - }) - .on('error', err => { - return cb(err); - }); - } - _validateKeyId(keyId) { if (!keyId) { return false; @@ -275,82 +227,28 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule { console.log(req); } - // _populateKeyIdInfo(keyId, info) { - // if (!_.isString(keyId)) { - // return false; - // } - - // const m = /^https?:\/\/.+\/(.+)#(main-key)$/.exec(keyId); - // if (!m || !m.length === 3) { - // return false; - // } - - // info.accountName = m[1]; - // info.keyType = m[2]; - // return true; - // } - _selfAsActorHandler(user, req, resp) { this.log.trace( { username: user.username }, `Serving ActivityPub Actor for "${user.username}"` ); - const userSelfUrl = selfUrl(this.webServer, user); + Actor.fromLocalUser(user, this.webServer, (err, actor) => { + if (err) { + // :TODO: Log me + return this.webServer.internalServerError(resp); + } - // :TODO: something like: Actor.makeActor(...) - const bodyJson = { - '@context': [ - 'https://www.w3.org/ns/activitystreams', - 'https://w3id.org/security/v1', - ], - id: userSelfUrl, - type: 'Person', - preferredUsername: user.username, - name: user.getSanitizedName('real'), - endpoints: { - sharedInbox: 'TODO', - }, - inbox: makeUserUrl(this.webServer, user, '/ap/users/') + '/inbox', - outbox: makeUserUrl(this.webServer, user, '/ap/users/') + '/outbox', - followers: makeUserUrl(this.webServer, user, '/ap/users/') + '/followers', - following: makeUserUrl(this.webServer, user, '/ap/users/') + '/following', - summary: user.getProperty(UserProps.AutoSignature) || '', - url: webFingerProfileUrl(this.webServer, user), + const body = JSON.stringify(actor); - // :TODO: we can start to define BBS related stuff with the community perhaps - // attachment: [ - // { - // name: 'SomeNetwork Address', - // type: 'PropertyValue', - // value: 'Mateo@21:1/121', - // }, - // ], - }; - - const publicKeyPem = user.getProperty(UserProps.PublicKeyMain); - if (!_.isEmpty(publicKeyPem)) { - bodyJson['publicKey'] = { - id: userSelfUrl + '#main-key', - owner: userSelfUrl, - publicKeyPem, + const headers = { + 'Content-Type': 'application/activity+json', + 'Content-Length': body.length, }; - } else { - this.log.warn( - { username: user.username }, - `No public key (${UserProps.PublicKeyMain}) for user "${user.username}"` - ); - } - const body = JSON.stringify(bodyJson); - - const headers = { - 'Content-Type': 'application/activity+json', - 'Content-Length': body.length, - }; - - resp.writeHead(200, headers); - return resp.end(body); + resp.writeHead(200, headers); + return resp.end(body); + }); } _standardSelfHandler(user, req, resp) {