More utils, tidy, and activitypub.js added

This commit is contained in:
Bryan Ashby 2023-01-03 20:32:09 -07:00
parent 127e9794ee
commit 6cea4269b2
No known key found for this signature in database
GPG Key ID: C2C1B501E4EFD994
4 changed files with 150 additions and 47 deletions

View File

@ -1,9 +1,47 @@
const { WellKnownLocations } = require('./servers/content/web'); const { WellKnownLocations } = require('./servers/content/web');
const User = require('./user');
const { Errors } = require('./enig_error');
const UserProps = require('./user_property');
exports.buildSelfUrl = buildSelfUrl; exports.makeUserUrl = makeUserUrl;
exports.webFingerProfileUrl = webFingerProfileUrl;
exports.selfUrl = selfUrl;
exports.userFromAccount = userFromAccount;
function buildSelfUrl(webServer, user, relPrefix) { function makeUserUrl(webServer, user, relPrefix) {
return webServer.buildUrl( return webServer.buildUrl(
WellKnownLocations.Internal + `${relPrefix}${user.username}` WellKnownLocations.Internal + `${relPrefix}${user.username}`
); );
} }
function webFingerProfileUrl(webServer, user) {
return webServer.buildUrl(WellKnownLocations.Internal + `/wf/@${user.username}`);
}
function selfUrl(webServer, user) {
return makeUserUrl(webServer, user, '/ap/users/');
}
function userFromAccount(accountName, cb) {
User.getUserIdAndName(accountName, (err, userId) => {
if (err) {
return cb(err);
}
User.getUser(userId, (err, user) => {
if (err) {
return cb(err);
}
const accountStatus = user.getPropertyAsNumber(UserProps.AccountStatus);
if (
User.AccountStatus.disabled == accountStatus ||
User.AccountStatus.inactive == accountStatus
) {
return cb(Errors.AccessDenied('Account disabled', ErrorReasons.Disabled));
}
return cb(null, user);
});
});
}

View File

@ -175,7 +175,7 @@ exports.getModule = class WebServerModule extends ServerModule {
return nextModule(err); return nextModule(err);
}); });
} catch (e) { } catch (e) {
logger.log.error(e, 'Exception caught web handler!'); Log.error(e, 'Exception caught web handler!');
return nextModule(e); return nextModule(e);
} }
}, },

View File

@ -0,0 +1,95 @@
const WebHandlerModule = require('../../../web_handler_module');
const {
makeUserUrl,
webFingerProfileUrl,
selfUrl,
userFromAccount,
} = require('../../../activitypub_util');
const UserProps = require('../../../user_property');
const { Errors } = require('../../../enig_error');
exports.moduleInfo = {
name: 'ActivityPub',
desc: 'Provides ActivityPub support',
author: 'NuSkooler, CognitiveGears',
packageName: 'codes.l33t.enigma.web.handler.activitypub',
};
exports.getModule = class ActivityPubWebHandler extends WebHandlerModule {
constructor() {
super();
}
init(cb) {
this.webServer = WebHandlerModule.getWebServer();
if (!this.webServer) {
return cb(Errors.UnexpectedState('Cannot access web server!'));
}
this.webServer.addRoute({
method: 'GET',
path: /^\/_enig\/ap\/users\/.+$/,
handler: this._selfUrlRequestHandler.bind(this),
});
return cb(null);
}
_selfUrlRequestHandler(req, resp) {
const accept = req.headers['accept'] || '*/*';
if (accept === 'application/activity+json') {
return this._selfActorHandler(req, resp);
}
}
_selfActorHandler(req, resp) {
const url = new URL(req.url, `https://${req.headers.host}`);
const accountName = url.pathname.substring(url.pathname.lastIndexOf('/') + 1);
userFromAccount(accountName, (err, user) => {
if (err) {
return this._notFound(resp);
}
const body = JSON.stringify({
'@context': [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1',
],
id: selfUrl(this.webServer, user),
type: 'Person',
preferredUsername: user.username,
name: user.getSanitizedName('real'),
endpoints: {
sharedInbox: 'TODO',
},
inbox: makeUserUrl(this.webServer, user, '/ap/users') + '/outbox',
outbox: makeUserUrl(this.webServer, user, '/ap/users') + '/inbox',
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),
publicKey: {},
// :TODO: we can start to define BBS related stuff with the community perhaps
});
const headers = {
'Content-Type': 'application/activity+json',
'Content-Length': body.length,
};
resp.writeHead(200, headers);
return resp.end(body);
});
}
_notFound(resp) {
this.webServer.respondWithError(
resp,
404,
'Resource not found',
'Resource Not Found'
);
}
};

View File

@ -2,7 +2,11 @@ const WebHandlerModule = require('../../../web_handler_module');
const Config = require('../../../config').get; const Config = require('../../../config').get;
const { Errors, ErrorReasons } = require('../../../enig_error'); const { Errors, ErrorReasons } = require('../../../enig_error');
const { WellKnownLocations } = require('../web'); const { WellKnownLocations } = require('../web');
const { buildSelfUrl } = require('../../../activitypub_util'); const {
selfUrl,
webFingerProfileUrl,
userFromAccount,
} = require('../../../activitypub_util');
const _ = require('lodash'); const _ = require('lodash');
const User = require('../../../user'); const User = require('../../../user');
@ -44,7 +48,7 @@ exports.getModule = class WebFingerWebHandler extends WebHandlerModule {
// we rely on the web server // we rely on the web server
this.webServer = WebHandlerModule.getWebServer(); this.webServer = WebHandlerModule.getWebServer();
if (!this.webServer || !this.webServer.isEnabled()) { if (!this.webServer) {
return cb(Errors.UnexpectedState('Cannot access web server!')); return cb(Errors.UnexpectedState('Cannot access web server!'));
} }
@ -107,13 +111,10 @@ exports.getModule = class WebFingerWebHandler extends WebHandlerModule {
const accountName = resource.substring(userPosition + 1); const accountName = resource.substring(userPosition + 1);
this._getUser(accountName, resp, (err, user) => { userFromAccount(accountName, (err, user) => {
if (err) { if (err) {
// |resp| already written to Log.warn({ error: err.message }, `Profile request failed: ${req.url}`);
return Log.warn( return this._notFound(resp);
{ error: err.message },
`Profile request failed: ${req.url}`
);
} }
this._getProfileTemplate((template, mimeType) => { this._getProfileTemplate((template, mimeType) => {
@ -204,10 +205,10 @@ exports.getModule = class WebFingerWebHandler extends WebHandlerModule {
); );
} }
this._getUser(accountName, resp, (err, user) => { userFromAccount(accountName, (err, user) => {
if (err) { if (err) {
// |resp| already written to Log.warn({ error: err.message }, `WebFinger failed: ${req.url}`);
return Log.warn({ error: err.message }, `WebFinger failed: ${req.url}`); return this._notFound(resp);
} }
const domain = this.webServer.getDomain(); const domain = this.webServer.getDomain();
@ -233,9 +234,7 @@ exports.getModule = class WebFingerWebHandler extends WebHandlerModule {
} }
_profileUrl(user) { _profileUrl(user) {
return this.webServer.buildUrl( return webFingerProfileUrl(this.webServer, user);
WellKnownLocations.Internal + `/wf/@${user.username}`
);
} }
_profilePageLink(user) { _profilePageLink(user) {
@ -248,7 +247,7 @@ exports.getModule = class WebFingerWebHandler extends WebHandlerModule {
} }
_selfUrl(user) { _selfUrl(user) {
return buildSelfUrl(this.webServer, user, '/ap/users/'); return selfUrl(this.webServer, user);
} }
// :TODO: only if ActivityPub is enabled // :TODO: only if ActivityPub is enabled
@ -288,33 +287,4 @@ exports.getModule = class WebFingerWebHandler extends WebHandlerModule {
'Resource Not Found' 'Resource Not Found'
); );
} }
_getUser(accountName, resp, cb) {
User.getUserIdAndName(accountName, (err, userId) => {
if (err) {
this._notFound(resp);
return cb(err);
}
User.getUser(userId, (err, user) => {
if (err) {
this._notFound(resp);
return cb(err);
}
const accountStatus = user.getPropertyAsNumber(UserProps.AccountStatus);
if (
User.AccountStatus.disabled == accountStatus ||
User.AccountStatus.inactive == accountStatus
) {
this._notFound(resp);
return cb(
Errors.AccessDenied('Account disabled', ErrorReasons.Disabled)
);
}
return cb(null, user);
});
});
}
}; };