Abilitiy to respond with 'Accept' Activity and ActivityPubSettings user props

This commit is contained in:
Bryan Ashby 2023-01-08 17:11:49 -07:00
parent e5b2beffcf
commit 3a70cc6939
No known key found for this signature in database
GPG Key ID: C2C1B501E4EFD994
6 changed files with 102 additions and 9 deletions

View File

@ -1,7 +1,10 @@
const { isString, isObject } = require('lodash');
const { v4: UUIDv4 } = require('uuid');
const { ActivityStreamsContext } = require('./activitypub_util');
module.exports = class Activity {
constructor(obj) {
this['@context'] = ActivityStreamsContext;
Object.assign(this, obj);
}
@ -28,7 +31,7 @@ module.exports = class Activity {
isValid() {
if (
this['@context'] !== 'https://www.w3.org/ns/activitystreams' ||
this['@context'] !== ActivityStreamsContext ||
!isString(this.id) ||
!isString(this.actor) ||
(!isString(this.object) && !isObject(this.object)) ||
@ -41,4 +44,19 @@ module.exports = class Activity {
return true;
}
// https://www.w3.org/TR/activitypub/#accept-activity-inbox
static makeAccept(webServer, localActor, followRequest, id = null) {
id = id || webServer.buildUrl(`/${UUIDv4()}`);
return new Activity({
type: 'Accept',
actor: localActor,
object: followRequest, // previous request Activity
});
}
sendTo(actorUrl, cb) {
// :TODO: https send |this| to actorUrl
}
};

View File

@ -11,6 +11,7 @@ const {
makeUserUrl,
selfUrl,
isValidLink,
ActivityStreamsContext,
} = require('./activitypub_util');
const Log = require('./logger').log;
@ -25,10 +26,11 @@ const isString = require('lodash/isString');
// https://www.w3.org/TR/activitypub/#actor-objects
module.exports = class Actor {
constructor(obj) {
this['@context'] = [ActivityStreamsContext];
if (obj) {
Object.assign(this, obj);
} else {
this['@context'] = ['https://www.w3.org/ns/activitystreams'];
this.id = '';
this.type = '';
this.inbox = '';
@ -41,7 +43,7 @@ module.exports = class Actor {
isValid() {
if (
!Array.isArray(this['@context']) ||
this['@context'][0] !== 'https://www.w3.org/ns/activitystreams'
this['@context'][0] !== ActivityStreamsContext
) {
return false;
}
@ -66,7 +68,7 @@ module.exports = class Actor {
const obj = {
'@context': [
'https://www.w3.org/ns/activitystreams',
ActivityStreamsContext,
'https://w3id.org/security/v1', // :TODO: add support
],
id: userSelfUrl,

View File

@ -0,0 +1,42 @@
const { selfUrl } = require('./activitypub_util');
const UserProps = require('./user_property');
module.exports = class ActivityPubSettings {
constructor(obj) {
this.enabled = true; // :TODO: fetch from +op config default
this.manuallyApproveFollowers = false;
this.hideSocialGraph = false; // followers, following
this.showRealName = false;
this.imageUrl = '';
this.iconUrl = '';
if (obj) {
Object.assign(this, obj);
}
}
static fromUser(user) {
if (!user.activityPubSettings) {
const settingsProp = user.getProperty(UserProps.ActivityPubSettings);
let settings;
try {
const parsed = JSON.parse(settingsProp);
settings = new ActivityPubSettings(parsed);
} catch (e) {
settings = new ActivityPubSettings();
}
user.activityPubSettings = settings;
}
return user.activityPubSettings;
}
persistToUserProperties(user, cb = null) {
return user.persistProperty(
UserProps.ActivityPubSettings,
JSON.stringify(this),
cb
);
}
};

View File

@ -2,6 +2,7 @@ const { WellKnownLocations } = require('./servers/content/web');
const User = require('./user');
const { Errors, ErrorReasons } = require('./enig_error');
const UserProps = require('./user_property');
const ActivityPubSettings = require('./activitypub_settings');
// deps
const _ = require('lodash');
@ -11,6 +12,7 @@ const fs = require('graceful-fs');
const paths = require('path');
const moment = require('moment');
exports.ActivityStreamsContext = 'https://www.w3.org/ns/activitystreams';
exports.isValidLink = isValidLink;
exports.makeUserUrl = makeUserUrl;
exports.webFingerProfileUrl = webFingerProfileUrl;
@ -52,6 +54,7 @@ function selfUrl(webServer, user) {
function accountFromSelfUrl(url) {
// https://some.l33t.enigma.board/_enig/ap/users/Masto -> Masto
// :TODO: take webServer, and just take path-to-users.length +1
return url.substring(url.lastIndexOf('/') + 1);
}
@ -78,6 +81,11 @@ function userFromAccount(accountName, cb) {
return cb(Errors.AccessDenied('Account disabled', ErrorReasons.Disabled));
}
const activityPubSettings = ActivityPubSettings.fromUser(user);
if (!activityPubSettings.enabled) {
return cb(Errors.AccessDenied('ActivityPub is not enabled for user'));
}
return cb(null, user);
});
});

View File

@ -1,16 +1,13 @@
const WebHandlerModule = require('../../../web_handler_module');
const {
makeUserUrl,
webFingerProfileUrl,
selfUrl,
userFromAccount,
getUserProfileTemplatedBody,
DefaultProfileTemplate,
accountFromSelfUrl,
} = require('../../../activitypub_util');
const UserProps = require('../../../user_property');
const Config = require('../../../config').get;
const Activity = require('../../../activitypub_activity');
const ActivityPubSettings = require('../../../activitypub_settings');
// deps
const _ = require('lodash');
@ -46,7 +43,6 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
this.webServer.addRoute({
method: 'POST',
//inbox: makeUserUrl(this.webServer, user, '/ap/users/') + '/inbox',
path: /^\/_enig\/ap\/users\/.+\/inbox$/,
handler: this._inboxPostHandler.bind(this),
});
@ -208,6 +204,31 @@ exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
// :TODO: return OK and kick off a async job of persisting and sending and 'Accepted'
//
// If the user blindly accepts Followers, we can persist
// and send an 'Accept' now. Otherwise, we need to queue this
// request for the user to review and decide what to do with
// at a later time.
//
const activityPubSettings = ActivityPubSettings.fromUser(user);
if (!activityPubSettings.manuallyApproveFollowers) {
//
// :TODO: Implement the queue
Actor.fromLocalUser(user, this.webServer, (err, localActor) => {
if (err) {
// :TODO:
return;
}
const accept = Activity.makeAccept(
this.webServer,
localActor,
activity
);
console.log(accept);
});
}
resp.writeHead(200, { 'Content-Type': 'text/html' });
return resp.end('');
});

View File

@ -69,4 +69,6 @@ module.exports = {
PublicKeyMain: 'public_key_main_rsa_pem', // RSA public key for user
PrivateKeyMain: 'private_key_main_rsa_pem', // RSA private key (corresponding to PublicKeyMain)
ActivityPubSettings: 'activity_pub_settings', // JSON object (above); see ActivityPubSettings in activitypub_settings.js
};