Borrow ESLint inspiration from AirBNB.

This commit is contained in:
Andrew Pietila 2023-10-08 23:14:04 -05:00
parent de41020b61
commit 4bb28d1c00
34 changed files with 1438 additions and 322 deletions

View file

@ -7,7 +7,13 @@
"parserOptions": {
"ecmaVersion": "latest"
},
// "extends": ["eslint:recommended"],
"extends": [
// "eslint:recommended",
// "airbnb-base"
],
"plugins": [
"import"
],
"rules": {
"indent": [
"error",
@ -38,6 +44,51 @@
{
"allowKeywords": false
}
],
"eol-last": "error",
"comma-dangle": [
"error",
"always-multiline"
],
"no-multi-spaces": "error",
"space-in-parens": [
"error",
"never"
],
"import/order": "error",
"no-return-await": "error",
"no-trailing-spaces": "error",
"padded-blocks": [
"error",
"never",
{
"allowSingleLineBlocks": false
}
],
"space-infix-ops": "error",
"radix": [
"error",
"always"
],
"object-curly-spacing": [
"error",
"never"
],
"space-before-function-paren": [
"error",
"always"
],
"one-var": [
"error",
"never"
],
"one-var-declaration-per-line": [
"error",
"always"
],
"template-curly-spacing": [
"error",
"never"
]
}
}

44
app.js
View file

@ -1,31 +1,32 @@
"use strict";
const express = require("express");
const { glob } = require("glob");
const { match: createPathMatch } = require("path-to-regexp");
const {glob} = require("glob");
const {match: createPathMatch} = require("path-to-regexp");
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser");
const databaseHandler = require("./lib/database-handler");
const qs = require("qs");
const databaseHandler = require("./lib/database-handler");
(async () => {
const app = express();
app.set("query parser", "extended");
app.use(bodyParser.json({ type: "application/*+json",
verify: function (req, _res, buf) {
app.use(bodyParser.json({
type: "application/*+json",
verify (req, _res, buf) {
req.rawBody = buf;
}
},
}));
app.use(bodyParser.json({
verify: function (req, _res, buf) {
verify (req, _res, buf) {
req.rawBody = buf;
}
},
}));
app.use(bodyParser.urlencoded({
extended: false,
verify: function (req, _res, buf) {
verify (req, _res, buf) {
req.rawBody = buf;
}
},
}));
app.use(cookieParser());
const routes = await glob("**/*.js", {
@ -34,42 +35,41 @@ const qs = require("qs");
});
const pathMatches = [];
app.use((req, _res, next) => {
console.log(`${req.path}${req.query?"?":""}${qs.stringify(req.query)}`);
console.log(`${req.path}${Object.keys(req.query).length ? "?" : ""}${qs.stringify(req.query)}`);
const requestUrl = new URL(req.url, "https://example.com/");
let candidateUrl = "";
let secondCandidateUrl = "";
for ( const pathMatch in pathMatches ) {
if ( pathMatches[pathMatch](requestUrl.pathname) ) {
for (const pathMatch in pathMatches) {
if (pathMatches[pathMatch](requestUrl.pathname)) {
// If we get an exact match, we don't need to process further.
return next();
} else if ( requestUrl.pathname.endsWith("/") && pathMatches[pathMatch](`${requestUrl.pathname}index`) ) {
} if (requestUrl.pathname.endsWith("/") && pathMatches[pathMatch](`${requestUrl.pathname}index`)) {
// If we end with a /, and the index path matches, lets do the index path, but prioritize the non-index path.
const secondRequestUrl = new URL(requestUrl);
secondRequestUrl.pathname = `${requestUrl.pathname}index`;
candidateUrl = secondRequestUrl.toString().substring(19);
} else if ( pathMatches[pathMatch](`${requestUrl.pathname}/index`) ) {
} else if (pathMatches[pathMatch](`${requestUrl.pathname}/index`)) {
// If we don't end with a /, and the /index path matches, lets do the /index path, but prioritize paths checked previously.
const secondRequestUrl = new URL(requestUrl);
secondRequestUrl.pathname = `${requestUrl.pathname}/index`;
secondCandidateUrl = secondRequestUrl.toString().substring(19);
}
}
if ( candidateUrl !== "" ) {
if (candidateUrl !== "") {
req.url = candidateUrl;
return next();
}
if ( secondCandidateUrl !== "" ) {
if (secondCandidateUrl !== "") {
req.url = secondCandidateUrl;
return next();
}
return next();
} );
for ( const routeScript in routes ) {
});
for (const routeScript in routes) {
const route = routes[routeScript].replace(/\.js$/, "");
console.log(route);
pathMatches.push( createPathMatch(`/${route}`));
pathMatches.push(createPathMatch(`/${route}`));
const routeObj = require(`./routes/${route}`);
if ( routeObj.route ) {
if (routeObj.route) {
routeObj.route(app.route(`/${route}`));
}
}

View file

@ -1,3 +1,3 @@
const database_handler = require("../lib/database-handler");
database_handler.createAccount(process.argv[2], process.argv[3], process.argv[4]);
database_handler.createAccount(process.argv[2], process.argv[3], process.argv[4]);

View file

@ -1,14 +1,14 @@
const databaseHandler = require("./database-handler");
const jsonld = require("jsonld");
const databaseHandler = require("./database-handler");
module.exports = {
jsonldCustomLoader: async (url, options) => {
const cache = databaseHandler.getJsonldSchemaCache(url);
if ( cache && cache.schema ) {
if (cache && cache.schema) {
return {
contextUrl: null,
document: JSON.parse(cache.schema),
documentUrl: url
documentUrl: url,
};
}
// TODO: Write HTTP client handler.
@ -18,17 +18,17 @@ module.exports = {
},
compactedForm: async (urlOrObj) => {
if ( typeof urlOrObj === "string" ) {
if (typeof urlOrObj === "string") {
try {
const url = new URL(urlOrObj);
return await jsonld.compact(await jsonld.expand(jsonld.documentLoaders.node()(url)), {});
return jsonld.compact(await jsonld.expand(jsonld.documentLoaders.node()(url)), {});
} catch (e) {
return await jsonld.compact(await jsonld.expand(JSON.parse(urlOrObj)), {});
return jsonld.compact(await jsonld.expand(JSON.parse(urlOrObj)), {});
}
} else {
return await jsonld.compact(await jsonld.expand(urlOrObj), {});
return jsonld.compact(await jsonld.expand(urlOrObj), {});
}
}
},
};
jsonld.documentLoader = module.exports.jsonldCustomLoader;
jsonld.documentLoader = module.exports.jsonldCustomLoader;

View file

@ -1,4 +1,5 @@
const db = require("better-sqlite3")("brainz-social.db");
db.pragma("journal_mode = WAL");
module.exports = {
@ -63,9 +64,13 @@ module.exports = {
},
createCsrfTokenAssociation: (...ids) => {
for ( const source_id in ids ) {
for ( const destination_id in ids ) {
db.prepare("INSERT INTO csrf_token_relations (source_id, destination_id) VALUES (?, ?)").run(source_id, destination_id);
for (const source_id in ids) {
if (Number.parseInt(ids[source_id], 10) === ids[source_id]) {
for (const destination_id in ids) {
if (Number.parseInt(ids[destination_id], 10) === ids[destination_id]) {
db.prepare("INSERT INTO csrf_token_relations (source_id, destination_id) VALUES (?, ?)").run(ids[source_id], ids[destination_id]);
}
}
}
}
},
@ -97,11 +102,10 @@ module.exports = {
getVapidKey: () => {
const vapidPublic = db.prepare("SELECT value FROM config WHERE key = vapid_key_public").get();
const vapidPrivate = db.prepare("SELECT value FROM config WHERE key = vapid_key_private").get();
if ( vapidPublic.value && vapidPrivate.value ) {
if (vapidPublic.value && vapidPrivate.value) {
return {public: vapidPublic, private: vapidPrivate};
} else {
return null;
}
return null;
},
setVapidKey: (publicKey, privateKey) => {
@ -110,7 +114,7 @@ module.exports = {
},
getJsonldSchemaCache: (url) => {
return db.prepare("SELECT schema FROM jsonld_schema_cache WHERE schema_uri = ? AND expires > ?").get(url, Math.floor(Date.now()/1000));
return db.prepare("SELECT schema FROM jsonld_schema_cache WHERE schema_uri = ? AND expires > ?").get(url, Math.floor(Date.now() / 1000));
},
storeJsonldSchemaCache: (url, schema, expiry) => {
@ -139,5 +143,5 @@ module.exports = {
storeW3idSecurityKey: (key_uri, publicKey, privateKey, expires) => {
db.prepare("INSERT INTO w3id_security_keys (key_uri, public_key, private_key) VALUES (?, ?, ?)").run(key_uri, publicKey, privateKey, expires);
}
};
},
};

View file

@ -1,11 +1,11 @@
module.exports = {
validate_exists: (body, fields) => {
if ( !body ) {
if (!body) {
return {error: "Validation failed, no body provided."};
}
for (const field in fields) {
if ( !body[fields[field]] ) {
if (!body[fields[field]]) {
return {error: `Validation failed, field ${field} missing from request.`};
}
}
@ -16,33 +16,35 @@ module.exports = {
validate_appropriate_scopes: (max_scope, scope_requested) => {
const max_scope_array_temp = max_scope.split(/(\s|\+)+/);
const max_scope_array = [];
for ( const scope in max_scope_array_temp ) {
max_scope_array.push(max_scope_array_temp[scope]);
if ( scope === "read" ) {
max_scope_array.push("read:accounts", "read:blocks", "read:bookmarks", "read:favorites", "read:filters", "read:follows", "read:lists", "read:mutes", "read:notifications", "read:search", "read:statuses");
}
for (const scope in max_scope_array_temp) {
if (scope.match(/[a-zA-Z0-9:]/)) {
max_scope_array.push(max_scope_array_temp[scope]);
if (scope === "read") {
max_scope_array.push("read:accounts", "read:blocks", "read:bookmarks", "read:favorites", "read:filters", "read:follows", "read:lists", "read:mutes", "read:notifications", "read:search", "read:statuses");
}
if ( scope === "write" ) {
max_scope_array.push("write:accounts", "write:blocks", "write:bookmarks", "write:conversations", "write:favourites", "write:filters", "write:follows", "write:lists", "write:media", "write:mutes", "write:notifications", "write:reports", "write:statuses");
}
if (scope === "write") {
max_scope_array.push("write:accounts", "write:blocks", "write:bookmarks", "write:conversations", "write:favourites", "write:filters", "write:follows", "write:lists", "write:media", "write:mutes", "write:notifications", "write:reports", "write:statuses");
}
if ( scope === "follow" ) {
max_scope_array.push("read:blocks", "write:blocks", "read:follows", "write:follows", "read:mutes", "write:mutes");
}
if (scope === "follow") {
max_scope_array.push("read:blocks", "write:blocks", "read:follows", "write:follows", "read:mutes", "write:mutes");
}
if ( scope === "admin:read" ) {
max_scope_array.push("admin:read:accounts", "admin:read:reports", "admin:read:domain_allows", "admin:read:domain_blocks", "admin:read:ip_blocks", "admin:read:email_domain_blocks", "admin:read:canonical_email_blocks");
}
if (scope === "admin:read") {
max_scope_array.push("admin:read:accounts", "admin:read:reports", "admin:read:domain_allows", "admin:read:domain_blocks", "admin:read:ip_blocks", "admin:read:email_domain_blocks", "admin:read:canonical_email_blocks");
}
if ( scope === "admin:write" ) {
max_scope_array.push("admin:write:accounts", "admin:write:reports", "admin:write:domain_allows", "admin:write:domain_blocks", "admin:write:ip_blocks", "admin:write:email_domain_blocks", "admin:write:canonical_email_blocks");
if (scope === "admin:write") {
max_scope_array.push("admin:write:accounts", "admin:write:reports", "admin:write:domain_allows", "admin:write:domain_blocks", "admin:write:ip_blocks", "admin:write:email_domain_blocks", "admin:write:canonical_email_blocks");
}
}
}
const scope_requested_array = scope_requested.split(/(\s|\+)+/);
for ( const scope in scope_requested_array ) {
if ( max_scope_array.includes(scope_requested_array[scope]) ) {
for (const scope in scope_requested_array) {
if (max_scope_array.includes(scope_requested_array[scope])) {
continue;
}
@ -50,5 +52,5 @@ module.exports = {
}
return true;
}
};
},
};

View file

@ -5,37 +5,37 @@ module.exports = {
auth_token: (needs_user, need_scopes) => {
return (req, res, next) => {
const token = databaseHandler.getTokenData(req.header("authentication").split(/\s+/)[1]);
if ( !token ) {
if (!token) {
res.status(401);
res.json({
error: "UNAUTHENTICATED"
error: "UNAUTHENTICATED",
});
res.end();
return;
}
if ( token.revoked ) {
if (token.revoked) {
res.status(401);
res.json({
error: "UNAUTHENTICATED"
error: "UNAUTHENTICATED",
});
res.end();
return;
}
if ( needs_user && token.user_id === 0 ) {
if (needs_user && token.user_id === 0) {
res.status(401);
res.json({
error: "INSUFFICIENT_AUTHENTICATION"
error: "INSUFFICIENT_AUTHENTICATION",
});
res.end();
return;
}
if ( !input_validate(token.scopes, need_scopes) ) {
if (!input_validate(token.scopes, need_scopes)) {
res.status(401);
res.json({
error: "INSUFFICIENT_SCOPE"
error: "INSUFFICIENT_SCOPE",
});
res.end();
return;
@ -49,5 +49,5 @@ module.exports = {
next();
};
}
};
},
};

View file

@ -10,34 +10,34 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db, callback) {
exports.up = function (db, callback) {
async.series([
db.createTable.bind(db, "applications", {
id: { type: "int", primaryKey: true, autoIncrement: true },
id: {type: "int", primaryKey: true, autoIncrement: true},
client_name: "string",
redirect_uri: "string",
scopes: "string",
website: "string",
client_id: { type: "string", unique: true },
client_secret: "string"
client_id: {type: "string", unique: true},
client_secret: "string",
}),
db.addIndex.bind(db, "applications", "clientIdIndex", ["client_id"], true)
db.addIndex.bind(db, "applications", "clientIdIndex", ["client_id"], true),
], callback);
};
exports.down = function(db, callback) {
exports.down = function (db, callback) {
async.series([
db.removeIndex.bind(db, "applications", "clientIdIndex"),
db.dropTable.bind(db, "applications")
db.dropTable.bind(db, "applications"),
], callback);
};
exports._meta = {
version: 1
version: 1,
};

View file

@ -8,24 +8,24 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db) {
exports.up = function (db) {
return db.createTable("config", {
id: { type: "int", primaryKey: true, autoIncrement: true },
key: { type: "string", unique: true },
value: "string"
id: {type: "int", primaryKey: true, autoIncrement: true},
key: {type: "string", unique: true},
value: "string",
});
};
exports.down = function(db) {
exports.down = function (db) {
return db.dropTable("config");
};
exports._meta = {
version: 1
version: 1,
};

View file

@ -10,13 +10,13 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db, callback) {
exports.up = function (db, callback) {
async.series([
db.createTable.bind(db, "oauth_tokens", {
id: {type: "int", primaryKey: true, autoIncrements: true},
@ -25,21 +25,21 @@ exports.up = function(db, callback) {
scopes: "string",
user_id: "int",
revoked: "boolean",
created_at: "int"
created_at: "int",
}),
db.addIndex.bind(db, "oauth_tokens", "oauth_token_index", ["token"]),
db.addIndex.bind(db, "oauth_tokens", "oauth_token_user_id_index", ["user_id"])
db.addIndex.bind(db, "oauth_tokens", "oauth_token_user_id_index", ["user_id"]),
], callback);
};
exports.down = function(db, callback) {
exports.down = function (db, callback) {
async.series([
db.removeIndex.bind(db, "oauth_tokens", "oauth_token_user_id_index"),
db.removeIndex.bind(db, "oauth_tokens", "oauth_token_index"),
db.dropTable("oauth_tokens")
db.dropTable("oauth_tokens"),
], callback);
};
exports._meta = {
version: 1
version: 1,
};

View file

@ -8,19 +8,19 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db, callback) {
exports.up = function (db, callback) {
db.createTable("accounts", {
id: { type: "int", primaryKey: true, autoIncrement: true },
id: {type: "int", primaryKey: true, autoIncrement: true},
username: {type: "string", unique: true},
email: "string",
password_hash: "string",
account_tier: "int"
account_tier: "int",
}, (result) => {
if (result) {
callback(result);
@ -30,10 +30,10 @@ exports.up = function(db, callback) {
});
};
exports.down = function(db) {
exports.down = function (db) {
return db.dropTable("accounts");
};
exports._meta = {
version: 1
version: 1,
};

View file

@ -10,32 +10,32 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db, callback) {
exports.up = function (db, callback) {
async.series([
db.createTable.bind(db, "cookies", {
id: { type: "int", primaryKey: true, autoIncrement: true },
id: {type: "int", primaryKey: true, autoIncrement: true},
cookie_value: {type: "string", unique: true},
created_at: "int",
user_id: "int",
revoked: "boolean"
revoked: "boolean",
}),
db.addIndex.bind(db, "cookies", "cookies_cookie_value_index", ["cookie_value"])
db.addIndex.bind(db, "cookies", "cookies_cookie_value_index", ["cookie_value"]),
], callback);
};
exports.down = function(db, callback) {
exports.down = function (db, callback) {
async.series([
db.removeIndex.bind(db, "cookies", "cookies_cookie_value_index"),
db.dropTable.bind(db, "cookies")
db.dropTable.bind(db, "cookies"),
], callback);
};
exports._meta = {
version: 1
version: 1,
};

View file

@ -8,24 +8,24 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db) {
exports.up = function (db) {
return db.createTable("csrf_token", {
id: { type: "int", primaryKey: true, autoIncrement: true },
id: {type: "int", primaryKey: true, autoIncrement: true},
url: "string",
created_at: "int"
created_at: "int",
});
};
exports.down = function(db) {
exports.down = function (db) {
return db.dropTable("csrf_token");
};
exports._meta = {
version: 1
version: 1,
};

View file

@ -8,24 +8,24 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db) {
exports.up = function (db) {
return db.createTable("csrf_token_relations", {
id: { type: "int", primaryKey: true, autoIncrement: true },
id: {type: "int", primaryKey: true, autoIncrement: true},
source_id: "int",
destination_id: "int"
destination_id: "int",
});
};
exports.down = function(db) {
exports.down = function (db) {
return db.dropTable("csrf_token_relations");
};
exports._meta = {
version: 1
version: 1,
};

View file

@ -8,20 +8,20 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db) {
exports.up = function (db) {
return db.addColumn("csrf_token", "cookie_value", "string");
};
exports.down = function(db) {
exports.down = function (db) {
return db.removeColumn("csrf_token", "cookie_value");
};
exports._meta = {
version: 1
version: 1,
};

View file

@ -10,13 +10,13 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db, callback) {
exports.up = function (db, callback) {
async.series([
db.createTable.bind(db, "oauth_code", {
id: {type: "int", primaryKey: true, autoIncrements: true},
@ -24,19 +24,19 @@ exports.up = function(db, callback) {
application_id: "int",
scopes: "string",
user_id: "int",
created_at: "int"
created_at: "int",
}),
db.addIndex.bind(db, "oauth_code", "oauth_code_index", ["code"])
db.addIndex.bind(db, "oauth_code", "oauth_code_index", ["code"]),
], callback);
};
exports.down = function(db, callback) {
exports.down = function (db, callback) {
async.series([
db.removeIndex.bind(db, "oauth_code", "oauth_code_index"),
db.dropTable.bind(db, "oauth_code")
db.dropTable.bind(db, "oauth_code"),
], callback);
};
exports._meta = {
version: 1
version: 1,
};

View file

@ -8,20 +8,20 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db) {
exports.up = function (db) {
return db.addColumn("oauth_code", "revoked", "boolean");
};
exports.down = function(db) {
exports.down = function (db) {
return db.removeColumn("oauth_code", "revoked");
};
exports._meta = {
version: 1
version: 1,
};

View file

@ -8,25 +8,25 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db) {
exports.up = function (db) {
return db.createTable("jsonld_schema_cache", {
id: {type: "int", primaryKey: true, autoIncrements: true},
schema_uri: "string",
schema: "string",
expires: "int"
expires: "int",
});
};
exports.down = function(db) {
exports.down = function (db) {
return db.dropTable("jsonld_schema_cache");
};
exports._meta = {
version: 1
version: 1,
};

View file

@ -8,27 +8,27 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db) {
exports.up = function (db) {
return db.createTable("activity_objects", {
id: {type: "int", primaryKey: true, autoIncrements: true},
object: "string",
type: "string",
local: "boolean",
uri_id: "string",
owner: "string"
owner: "string",
});
};
exports.down = function(db) {
exports.down = function (db) {
return db.dropTable("activity_objects");
};
exports._meta = {
version: 1
version: 1,
};

View file

@ -8,20 +8,20 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db) {
exports.up = function (db) {
return db.addColumn("activity_objects", "created_at", "string");
};
exports.down = function(db) {
exports.down = function (db) {
return db.removeColumn("activity_objects", "created_at");
};
exports._meta = {
version: 1
version: 1,
};

View file

@ -8,26 +8,26 @@ let seed;
* We receive the dbmigrate dependency from dbmigrate initially.
* This enables us to not have to rely on NODE_PATH.
*/
exports.setup = function(options, seedLink) {
exports.setup = function (options, seedLink) {
dbm = options.dbmigrate;
type = dbm.dataType;
seed = seedLink;
};
exports.up = function(db) {
exports.up = function (db) {
return db.createTable("w3id_security_keys", {
id: {type: "int", primaryKey: true, autoIncrements: true},
key_uri: "string",
public_key: "string",
private_key: "string",
expires: "int"
expires: "int",
});
};
exports.down = function(db) {
exports.down = function (db) {
return db.dropTable("w3id_security_keys");
};
exports._meta = {
version: 1
version: 1,
};

1080
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -9,6 +9,7 @@
"author": "Andrew Pietila <a.pietila@protonmail.com>",
"license": "WTFPL",
"dependencies": {
"async": "^3.2.4",
"bcrypt": "^5.1.1",
"better-sqlite3": "^8.6.0",
"body-parser": "^1.20.2",
@ -24,6 +25,8 @@
},
"type": "commonjs",
"devDependencies": {
"eslint": "^8.50.0"
"eslint": "^8.51.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.28.1"
}
}

View file

@ -1,7 +1,7 @@
const bcrypt = require("bcrypt");
const crypto = require("crypto");
const databaseHandler = require("../../../lib/database-handler");
const input_validate = require( "../../../lib/input_validate" );
const input_validate = require("../../../lib/input_validate");
const auth = require("../../../middleware/auth");
module.exports = {
@ -10,14 +10,14 @@ module.exports = {
const authToken = req.header("Authorization").split(/\s+/)[1];
const validation_result = input_validate.validate_exists(req.body, ["username", "email", "password", "agreement", "locale"]);
if ( validation_result !== true ) {
if (validation_result !== true) {
res.status(422);
res.json(validation_result);
return;
}
const username = req.body.username;
if ( databaseHandler.getAccountByUsername(username) ) {
if (databaseHandler.getAccountByUsername(username)) {
res.status(422);
res.json({error: "Validation failed, username taken.", details: {username: {error: "ERR_TAKEN", description: "Username taken."}}});
return;
@ -33,7 +33,7 @@ module.exports = {
const userObject = databaseHandler.getAccountByUsername(username);
const userToken = crypto.randomBytes(32).toString("base64");
const created_at = Math.floor(Date.now()/1000);
const created_at = Math.floor(Date.now() / 1000);
const application = databaseHandler.getApplicationById(authToken.application_id);
databaseHandler.createToken(userToken, application.scopes, application.id, userObject.id, created_at);
res.status(200);
@ -41,8 +41,8 @@ module.exports = {
access_token: userToken,
token_type: "Bearer",
scope: application.scopes,
created_at
created_at,
});
});
})
};
}),
};

View file

@ -9,81 +9,81 @@ module.exports = {
const account = databaseHandler.getAccountByToken(token.token);
const accountActivityRow = databaseHandler.getAccountActivityByAccount(account.id);
let accountActivity = {};
if ( accountActivityRow ) {
if (accountActivityRow) {
accountActivity = JSON.parse(accountActivityRow.object);
} else {
const keyPair = crypto.generateKeyPairSync("rsa", {
modulusLength: 4096,
publicKeyEncoding: {
type: "spki",
format: "pem"
format: "pem",
},
privateKeyEncoding: {
type: "pkcs8",
format: "pem"
}
format: "pem",
},
});
databaseHandler.storeW3idSecurityKey(`https://${req.headers.host}/users/${account.username}#main-key`, keyPair.publicKey, keyPair.privateKey, (Math.floor(Date.now()/1000)+(86400*90)));
databaseHandler.storeW3idSecurityKey(`https://${req.headers.host}/users/${account.username}#main-key`, keyPair.publicKey, keyPair.privateKey, (Math.floor(Date.now() / 1000) + (86400 * 90)));
accountActivity = {
"@id": `https://${req.headers.host}/users/${account.username}`,
"@type": "https://www.w3.org/ns/activitystreams#Person",
"http://joinmastodon.org/ns#devices": {
"@id": `https://${req.headers.host}/users/${account.username}/collections/devices`
"@id": `https://${req.headers.host}/users/${account.username}/collections/devices`,
},
"http://joinmastodon.org/ns#discoverable": true,
"http://joinmastodon.org/ns#featured": {
"@id": `https://${req.headers.host}/users/${account.username}/collections/featured`
"@id": `https://${req.headers.host}/users/${account.username}/collections/featured`,
},
"http://joinmastodon.org/ns#featuredTags": {
"@id": `https://${req.headers.host}/users/${account.username}/collections/tags`
"@id": `https://${req.headers.host}/users/${account.username}/collections/tags`,
},
"http://joinmastodon.org/ns#indexable": true,
"http://joinmastodon.org/ns#memorial": false,
"http://www.w3.org/ns/ldp#inbox": {
"@id": `https://${req.headers.host}/users/${account.username}/inbox`
"@id": `https://${req.headers.host}/users/${account.username}/inbox`,
},
"https://w3id.org/security#publicKey": {
"@id": `https://${req.headers.host}/users/${account.username}#main-key`,
"https://w3id.org/security#owner": {
"@id": `https://${req.headers.host}/users/${account.username}`
"@id": `https://${req.headers.host}/users/${account.username}`,
},
"https://w3id.org/security#publicKeyPem": keyPair.publicKey
"https://w3id.org/security#publicKeyPem": keyPair.publicKey,
},
"https://www.w3.org/ns/activitystreams#endpoints": {
"https://www.w3.org/ns/activitystreams#sharedInbox": {
"@id": `https://${req.headers.host}/inbox`
}
"@id": `https://${req.headers.host}/inbox`,
},
},
"https://www.w3.org/ns/activitystreams#followers": {
"@id": `https://${req.headers.host}/users/${account.username}/followers`
"@id": `https://${req.headers.host}/users/${account.username}/followers`,
},
"https://www.w3.org/ns/activitystreams#following": {
"@id": `https://${req.headers.host}/users/${account.username}/following`
"@id": `https://${req.headers.host}/users/${account.username}/following`,
},
"https://www.w3.org/ns/activitystreams#icon": {
"@type": "https://www.w3.org/ns/activitystreams#Image",
"https://www.w3.org/ns/activitystreams#mediaType": "image/png",
"https://www.w3.org/ns/activitystreams#url": {
"@id": `https://${req.headers.host}/res/avatar_not_found.png`
}
"@id": `https://${req.headers.host}/res/avatar_not_found.png`,
},
},
"https://www.w3.org/ns/activitystreams#manuallyApprovesFollowers": false,
"https://www.w3.org/ns/activitystreams#name": account.username,
"https://www.w3.org/ns/activitystreams#outbox": {
"@id": `https://${req.headers.host}/users/${account.username}/outbox`
"@id": `https://${req.headers.host}/users/${account.username}/outbox`,
},
"https://www.w3.org/ns/activitystreams#preferredUsername": account.username,
"https://www.w3.org/ns/activitystreams#published": {
"@type": "http://www.w3.org/2001/XMLSchema#dateTime",
"@value": new Date(Date.now()).toISOString()
"@value": new Date(Date.now()).toISOString(),
},
"https://www.w3.org/ns/activitystreams#summary": "",
"https://www.w3.org/ns/activitystreams#tag": [],
"https://www.w3.org/ns/activitystreams#url": {
"@id": `https://${req.headers.host}/@${account.username}`
}
"@id": `https://${req.headers.host}/@${account.username}`,
},
};
}
res.status(200);
@ -113,10 +113,10 @@ module.exports = {
fields: [],
privacy: "public",
sensitive: false,
language: "en"
}
language: "en",
},
});
return;
});
}
};
},
};

View file

@ -1,23 +1,24 @@
const crypto = require("crypto");
const webpush = require("web-push");
const database_handler = require("../../../lib/database-handler");
const input_validate = require("../../../lib/input_validate");
const webpush = require("web-push");
module.exports = {
route: ((routeObj) => {
routeObj.post((req, res) => {
const validation_result = input_validate.validate_exists(req.body, ["client_name", "redirect_uris"]);
if ( validation_result !== true ) {
if (validation_result !== true) {
res.status(422);
res.json(validation_result);
return;
}
let scopes, website;
let scopes;
let website;
const client_name = req.body.client_name;
const redirect_uris = req.body.redirect_uris;
if ( redirect_uris !== "urn:ietf:wg:oauth:2.0:oob" ) {
if (redirect_uris !== "urn:ietf:wg:oauth:2.0:oob") {
let url;
try {
url = new URL(redirect_uris);
@ -35,23 +36,23 @@ module.exports = {
scopes = "read";
if ( req.body.scopes ) {
if (req.body.scopes) {
scopes = req.body.scopes;
}
website = null;
if ( req.body.website ) {
if (req.body.website) {
website = req.body.website;
}
let vapid_key;
// Not that anything is implemented yet regarding Web Push/VAPID/whatever,
// this is a required item per the Mastodon API documentation.
vapid_key = database_handler.getConfig("vapid_key_public");
if ( !vapid_key ) {
if (!vapid_key) {
const vapidKeys = webpush.generateVAPIDKeys();
database_handler.setConfig("vapid_key_public", vapidKeys.publicKey);
database_handler.setConfig("vapid_key_private", vapidKeys.privateKey);
@ -69,10 +70,10 @@ module.exports = {
website,
vapid_key,
client_id,
client_secret
client_secret,
});
return;
});
})
};
}),
};

View file

@ -1,6 +1,6 @@
const webpush = require("web-push");
const databaseHandler = require("../../../../lib/database-handler");
const auth_middleware = require("../../../../middleware/auth");
const webpush = require("web-push");
module.exports = {
route: (routeObj) => {
@ -9,10 +9,10 @@ module.exports = {
const application = databaseHandler.getApplicationByToken(token);
let vapid_key = databaseHandler.getVapidKey();
if ( vapid_key ) {
if (vapid_key) {
vapid_key = vapid_key["public"];
}
if ( !vapid_key ) {
if (!vapid_key) {
const vapidKeys = webpush.generateVAPIDKeys();
databaseHandler.setVapidKey(vapidKeys.publicKey, vapidKeys.privateKey);
vapid_key = vapidKeys.publicKey;
@ -22,9 +22,9 @@ module.exports = {
res.json({
name: application.name,
website: application.website,
vapid_key: vapid_key["public"]
vapid_key: vapid_key["public"],
});
return;
});
}
};
},
};

View file

@ -5,5 +5,5 @@ module.exports = {
res.json({});
return;
});
}
};
},
};

View file

@ -1,21 +1,21 @@
const databaseHandler = require("../lib/database-handler");
const crypto = require("crypto");
const input_validate = require("../lib/input_validate");
const bcrypt = require("bcrypt");
const databaseHandler = require("../lib/database-handler");
const input_validate = require("../lib/input_validate");
module.exports = {
route: (routeObj) => {
routeObj.get((req, res) => {
if ( req.cookies && req.cookies.auth !== undefined ) {
if (req.cookies && req.cookies.auth !== undefined) {
const cookie_db = databaseHandler.checkAuthCookie(req.cookies.auth);
if ( cookie_db && !cookie_db.revoked && cookie_db.created_at + 86400 > Math.floor(Date.now()/1000)) {
if (cookie_db && !cookie_db.revoked && cookie_db.created_at + 86400 > Math.floor(Date.now() / 1000)) {
const new_cookie_value = crypto.randomBytes(32).toString("base64");
databaseHandler.revokeAuthCookie(req.cookies.auth);
databaseHandler.createAuthCookie(new_cookie_value, Math.floor(Date.now()/1000), cookie_db.user_id);
databaseHandler.createAuthCookie(new_cookie_value, Math.floor(Date.now() / 1000), cookie_db.user_id);
// TODO: Set correct redirect status.
res.status(302);
res.cookie("auth", new_cookie_value, { maxAge: 86400000, httpOnly: true});
if ( req.query && req.query.redirect_to ) {
res.cookie("auth", new_cookie_value, {maxAge: 86400000, httpOnly: true});
if (req.query && req.query.redirect_to) {
// TODO: This should be validated so as to not go off-site without warning the user.
res.redirect(req.query.redirect_to);
} else {
@ -26,21 +26,21 @@ module.exports = {
}
}
res.status(200);
res.send(`<html><body><form action="/login" method="POST"><label for="username" >Username: </label><input type="text" id="username" name="username" /><br /><label for="password" >Password: </label><input type="password" id="password" name="password" /><br />${req.query.redirect_to ? `<input type="hidden" id="redirect_to" name="redirect_to" value="${req.query.redirect_to}" />` : "" }<input type="submit" /></form></body></html>`);
res.send(`<html><body><form action="/login" method="POST"><label for="username" >Username: </label><input type="text" id="username" name="username" /><br /><label for="password" >Password: </label><input type="password" id="password" name="password" /><br />${req.query.redirect_to ? `<input type="hidden" id="redirect_to" name="redirect_to" value="${req.query.redirect_to}" />` : ""}<input type="submit" /></form></body></html>`);
res.end();
});
routeObj.post((req, res) => {
if ( req.cookies && req.cookies.auth ) {
if (req.cookies && req.cookies.auth) {
const cookie_db = databaseHandler.checkAuthCookie(req.cookies.auth);
if ( cookie_db && !cookie_db.revoked && cookie_db.created_at + 86400 > Math.floor(Date.now()/1000)) {
if (cookie_db && !cookie_db.revoked && cookie_db.created_at + 86400 > Math.floor(Date.now() / 1000)) {
const new_cookie_value = crypto.randomBytes(32).toString("base64");
databaseHandler.revokeAuthCookie(req.cookies.auth);
databaseHandler.createAuthCookie(new_cookie_value, Math.floor(Date.now()/1000), cookie_db.user_id);
databaseHandler.createAuthCookie(new_cookie_value, Math.floor(Date.now() / 1000), cookie_db.user_id);
// TODO: Set correct redirect status.
res.status(302);
res.cookie("auth", new_cookie_value, {maxAge: 86400000, httpOnly: true});
if ( req.body && req.body.redirect_to ) {
if (req.body && req.body.redirect_to) {
// TODO: External site warning.
res.redirect(req.body.redirect_to);
} else {
@ -53,14 +53,14 @@ module.exports = {
const validation_result = input_validate.validate_exists(req.body, ["username", "password"]);
if ( validation_result !== true ) {
if (validation_result !== true) {
res.status(422);
res.json(validation_result);
return;
}
const user_obj = databaseHandler.getAccountByUsername(req.body.username);
if ( !user_obj ) {
if (!user_obj) {
res.status(401);
// TODO: redirect_url
res.send("<html><body>AUTH FAILURE<br /><form action=\"/login\" method=\"POST\"><label for=\"username\" >Username: </label><input type=\"text\" id=\"username\" name=\"username\" /><br /><label for=\"password\" >Password: </label><input type=\"password\" id=\"password\" name=\"password\" /><br /><input type=\"submit\" /></form></body></html>");
@ -68,13 +68,13 @@ module.exports = {
return;
}
if ( bcrypt.compareSync(req.body.password, user_obj.password_hash) ) {
if (bcrypt.compareSync(req.body.password, user_obj.password_hash)) {
const new_cookie_value = crypto.randomBytes(32).toString("base64");
databaseHandler.createAuthCookie(new_cookie_value, Math.floor(Date.now()/1000), user_obj.id);
databaseHandler.createAuthCookie(new_cookie_value, Math.floor(Date.now() / 1000), user_obj.id);
// TODO: Set correct redirect status.
res.status(302);
res.cookie("auth", new_cookie_value, {maxAge: 86400000, httpOnly: true});
if ( req.body && req.body.redirect_to ) {
if (req.body && req.body.redirect_to) {
// TODO: External site warning.
res.redirect(req.body.redirect_to);
} else {
@ -90,5 +90,5 @@ module.exports = {
res.end();
return;
});
}
};
},
};

View file

@ -6,7 +6,7 @@ module.exports = {
version: 2.0,
software: {
name: "Brainz Social",
version: "0.0.1"
version: "0.0.1",
},
protocols: [],
services: [],
@ -15,13 +15,13 @@ module.exports = {
users: {
total: 0,
activeHalfYear: 0,
activeMonth: 0
activeMonth: 0,
},
localPosts: 0,
localComments: 0
localComments: 0,
},
metadata: {}
metadata: {},
});
});
}
};
},
};

View file

@ -1,19 +1,19 @@
const crypto = require("crypto");
const qs = require("qs");
const databaseHandler = require("../../lib/database-handler");
const input_validate = require("../../lib/input_validate");
const qs = require("qs");
module.exports = {
route: (routeObj) => {
routeObj.get((req, res) => {
const validation_result = input_validate.validate_exists(req.query, ["response_type", "client_id", "redirect_uri"]);
if ( validation_result !== true ) {
if (validation_result !== true) {
res.status(422);
res.send(`<html><body>Someone done goof'ed with your oauth request.<br />${validation_result.error}</body></html>`);
res.end();
return;
}
if ( !req.cookies || !req.cookies.auth ) {
if (!req.cookies || !req.cookies.auth) {
const new_redirect_url = new URL("http://example.com");
new_redirect_url.host = req.headers.host;
new_redirect_url.pathname = req.path;
@ -27,59 +27,57 @@ module.exports = {
res.end();
} else {
const cookie_db = databaseHandler.checkAuthCookie(req.cookies.auth);
if ( cookie_db != null && !cookie_db.revoked && (cookie_db.created_at + 86400) > Math.floor(Date.now()/1000)) {
if (cookie_db != null && !cookie_db.revoked && (cookie_db.created_at + 86400) > Math.floor(Date.now() / 1000)) {
const new_cookie_value = crypto.randomBytes(32).toString("base64");
databaseHandler.revokeAuthCookie(req.cookies.auth);
databaseHandler.createAuthCookie(new_cookie_value, Math.floor(Date.now()/1000), cookie_db.user_id);
databaseHandler.createAuthCookie(new_cookie_value, Math.floor(Date.now() / 1000), cookie_db.user_id);
const application_obj = databaseHandler.getApplication(req.query.client_id);
let scope = "read";
if ( req.query.scope ) {
if (req.query.scope) {
scope = req.query.scope;
}
if ( !input_validate.validate_appropriate_scopes(application_obj.scopes, scope) ) {
if (!input_validate.validate_appropriate_scopes(application_obj.scopes, scope)) {
res.status(422);
res.cookie("auth", new_cookie_value);
// TODO: XSS
res.send(`<html><body>Error: invalid scopes requested. Contact the author of ${application_obj.client_name} to correct their scope request.</body></html>`);
res.end();
return;
} else {
res.status(200);
res.cookie("auth", new_cookie_value);
const approve_url = new URL("http://example.com");
approve_url.host = req.headers.host;
approve_url.pathname = "/oauth/authorize/yes";
approve_url.searchParams.set("response_type", req.query.response_type);
approve_url.searchParams.set("client_id", req.query.client_id);
approve_url.searchParams.set("redirect_uri", req.query.redirect_uri);
approve_url.searchParams.set("scope", scope);
const approve_csrf = crypto.randomBytes(32).toString("base64");
approve_url.searchParams.set("csrf_token", approve_csrf);
const deny_url = new URL(approve_url);
deny_url.pathname = "/oauth/authorize/no";
const deny_csrf = crypto.randomBytes(32).toString("base64");
deny_url.searchParams.set("csrf_token", deny_csrf);
const approve_csrf_id = databaseHandler.createCsrfToken(approve_url.toString(), Math.floor(Date.now()/1000), new_cookie_value);
const deny_csrf_id = databaseHandler.createCsrfToken(deny_url.toString(), Math.floor(Date.now()/1000), new_cookie_value);
databaseHandler.createCsrfTokenAssociation(approve_csrf_id, deny_csrf_id);
res.send(`<html><body>The ${application_obj.client_name} application has requested the following scopes:<br /><ul><li>${scope.split(/(\s|\+)+/).join("</li><li>")}</li></ul><br /><a href="${approve_url.toString()}" >Approve</a><br /><a href="${deny_url.toString()}" >Deny</a></body></html>`);
res.end();
return;
}
} else {
const new_redirect_url = new URL("http://example.com");
new_redirect_url.host = req.headers.host;
new_redirect_url.pathname = req.path;
new_redirect_url.search = qs.stringify(req.query);
const redirecting_to = new URL("http://example.com");
redirecting_to.host = req.headers.host;
redirecting_to.pathname = "/login";
redirecting_to.searchParams.set("redirect_to", new_redirect_url.toString());
res.status(302);
res.redirect(redirecting_to.toString());
}
res.status(200);
res.cookie("auth", new_cookie_value);
const approve_url = new URL("http://example.com");
approve_url.host = req.headers.host;
approve_url.pathname = "/oauth/authorize/yes";
approve_url.searchParams.set("response_type", req.query.response_type);
approve_url.searchParams.set("client_id", req.query.client_id);
approve_url.searchParams.set("redirect_uri", req.query.redirect_uri);
approve_url.searchParams.set("scope", scope);
const approve_csrf = crypto.randomBytes(32).toString("base64");
approve_url.searchParams.set("csrf_token", approve_csrf);
const deny_url = new URL(approve_url);
deny_url.pathname = "/oauth/authorize/no";
const deny_csrf = crypto.randomBytes(32).toString("base64");
deny_url.searchParams.set("csrf_token", deny_csrf);
const approve_csrf_id = databaseHandler.createCsrfToken(approve_url.toString(), Math.floor(Date.now() / 1000), new_cookie_value);
const deny_csrf_id = databaseHandler.createCsrfToken(deny_url.toString(), Math.floor(Date.now() / 1000), new_cookie_value);
databaseHandler.createCsrfTokenAssociation(approve_csrf_id, deny_csrf_id);
res.send(`<html><body>The ${application_obj.client_name} application has requested the following scopes:<br /><ul><li>${scope.split(/(\s|\+)+/).join("</li><li>")}</li></ul><br /><a href="${approve_url.toString()}" >Approve</a><br /><a href="${deny_url.toString()}" >Deny</a></body></html>`);
res.end();
}
return;
}
const new_redirect_url = new URL("http://example.com");
new_redirect_url.host = req.headers.host;
new_redirect_url.pathname = req.path;
new_redirect_url.search = qs.stringify(req.query);
const redirecting_to = new URL("http://example.com");
redirecting_to.host = req.headers.host;
redirecting_to.pathname = "/login";
redirecting_to.searchParams.set("redirect_to", new_redirect_url.toString());
res.status(302);
res.redirect(redirecting_to.toString());
res.end();
}
});
}
};
},
};

View file

@ -1,6 +1,6 @@
const crypto = require("crypto");
const databaseHandler = require("../../../lib/database-handler");
const qs = require("qs");
const databaseHandler = require("../../../lib/database-handler");
module.exports = {
route: (routeObj) => {
@ -10,7 +10,7 @@ module.exports = {
request_url.pathname = "/oauth/authorize/yes";
request_url.search = new URLSearchParams(qs.stringify(req.query));
const cookie_object = databaseHandler.checkAuthCookie(req.cookies.auth);
if ( cookie_object.revoked || (cookie_object.created_at + 86400) < Math.floor(Date.now()/1000) ) {
if (cookie_object.revoked || (cookie_object.created_at + 86400) < Math.floor(Date.now() / 1000)) {
res.status(302);
const redirect_to = new URL("http://example.com");
redirect_to.host = req.headers.host;
@ -21,13 +21,13 @@ module.exports = {
}
databaseHandler.revokeAuthCookie(cookie_object.cookie_value);
const new_cookie = crypto.randomBytes(32).toString("base64");
databaseHandler.createAuthCookie(new_cookie, Math.floor(Date.now()/1000), cookie_object.id);
databaseHandler.createAuthCookie(new_cookie, Math.floor(Date.now() / 1000), cookie_object.id);
const csrf_token = databaseHandler.getCsrfToken(request_url.toString());
if ( csrf_token.cookie_value === req.cookies.auth && (Math.floor(Date.now()/1000) < (csrf_token.created_at + 300)) ) {
if (csrf_token.cookie_value === req.cookies.auth && (Math.floor(Date.now() / 1000) < (csrf_token.created_at + 300))) {
const response_code = crypto.randomBytes(32).toString("base64");
const application_object = databaseHandler.getApplication(req.query.client_id);
databaseHandler.createOauthCode(response_code, application_object.id, cookie_object.user_id, req.query.scopes, Math.floor(Date.now()/1000));
if ( req.query.redirect_uri === "urn:ietf:wg:oauth:2.0:oob" ) {
databaseHandler.createOauthCode(response_code, application_object.id, cookie_object.user_id, req.query.scopes, Math.floor(Date.now() / 1000));
if (req.query.redirect_uri === "urn:ietf:wg:oauth:2.0:oob") {
res.status(200);
res.cookie("auth", new_cookie);
res.send(`<html><body>Your code: &lt; ${response_code} &gt;. Valid for 300 seconds.</body></html>`);
@ -54,5 +54,5 @@ module.exports = {
res.end();
return;
});
}
},
};

View file

@ -1,11 +1,11 @@
const databaseHandler = require("../../lib/database-handler");
const input_validate = require( "../../lib/input_validate");
const input_validate = require("../../lib/input_validate");
module.exports = {
route: (routeObj) => {
routeObj.post((req, res) => {
const validation_result = input_validate(req.body, ["client_id", "client_secret", "token"]);
if ( validation_result !== true ) {
if (validation_result !== true) {
res.status(422);
res.json(validation_result);
return;
@ -14,13 +14,13 @@ module.exports = {
const tokenData = databaseHandler.getTokenData(req.body.token);
const application = databaseHandler.getApplication(req.body.client_id);
if ( application.client_secret !== req.body.client_secret ) {
if (application.client_secret !== req.body.client_secret) {
res.status(401);
res.json({error: "unauthorized_client", error_description: "You are not authorized to revoke this token."});
return;
}
if ( tokenData.application_id !== application.id ) {
if (tokenData.application_id !== application.id) {
res.status(401);
res.json({error: "unauthorized_client", error_description: "You are not authorized to revoke this token."});
return;
@ -30,5 +30,5 @@ module.exports = {
res.status(200);
res.json({});
});
}
};
},
};

View file

@ -8,7 +8,7 @@ module.exports = {
let scope;
const validation_result = input_validate.validate_exists(req.body, ["grant_type", "client_id", "client_secret", "redirect_uri"]);
if ( validation_result !== true ) {
if (validation_result !== true) {
res.status(422);
res.json(validation_result);
return;
@ -22,14 +22,13 @@ module.exports = {
scope = "read";
if ( req.body.scope != undefined ) {
if (req.body.scope != undefined) {
scope = req.body.scope.replace(/\+/, " ");
}
const application = databaseHandler.getApplication(client_id);
if ( !application.client_id ) {
console.log("UNREGISTERED");
if (!application.client_id) {
res.status(422);
res.json({error: "UNREGISTERED_APPLICATION"});
res.end();
@ -37,64 +36,64 @@ module.exports = {
}
let application_scope = "read";
if ( application.scopes != undefined ) {
if (application.scopes != undefined) {
application_scope = application.scopes;
}
if ( client_secret !== application.client_secret ) {
if (client_secret !== application.client_secret) {
res.status(401);
res.json({error: "invalid_client", error_description: "client_id not known or does not match client_secret."});
return;
}
if ( !input_validate.validate_appropriate_scopes(application_scope, scope) ) {
if (!input_validate.validate_appropriate_scopes(application_scope, scope)) {
res.status(422);
res.json({error: "Requested scopes in transaction application did not previously declare."});
return;
}
if ( grant_type === "authorization_code" ) {
if (grant_type === "authorization_code") {
const code = databaseHandler.getOauthCode(req.body.code);
databaseHandler.revokeOauthCode(req.query.code);
if ( code.revoked ) {
if (code.revoked) {
res.status(401);
res.json({error: "used_code", error_description: "Oauth codes are single use."});
return;
}
if ( (code.created_at + 60) < Math.floor(Date.now()/1000) ) {
if ((code.created_at + 60) < Math.floor(Date.now() / 1000)) {
res.status(401);
res.json({error: "expired_code", error_description: "Oauth codes must be used within 60 seconds of issuance."});
return;
}
const token = crypto.randomBytes(32).toString("base64");
const created_at = Math.floor(Date.now()/1000);
const created_at = Math.floor(Date.now() / 1000);
databaseHandler.createToken(token, scope, application.id, code.user_id, created_at);
res.status(200);
res.json({
access_token: token,
token_type: "Bearer",
scope: req.query.scope,
created_at
created_at,
});
return;
} else if ( grant_type !== "client_credentials" ) {
} if (grant_type !== "client_credentials") {
res.status(422);
res.json({error: "Validation failed, unrecognized grant_type"});
return;
}
const token = crypto.randomBytes(32).toString("base64");
const created_at = Math.floor(Date.now()/1000);
const created_at = Math.floor(Date.now() / 1000);
databaseHandler.createToken(token, scope, application.id, 0, created_at);
res.status(200);
res.json({
access_token: token,
token_type: "Bearer",
scope,
created_at
created_at,
});
});
}
};
},
};