Compare commits
3 Commits
22328db33a
...
5e1c176ac6
Author | SHA1 | Date | |
---|---|---|---|
5e1c176ac6 | |||
4f12444a9d | |||
3b63f02dd7 |
@ -64,7 +64,7 @@ class UserDatabase extends AbstractUserDatabase {
|
||||
if (id.includes('temp'))
|
||||
return null;
|
||||
|
||||
const data = await this.db.findOne(this._userColllection, { _id: ObjectId(id) });
|
||||
const data = await this.db.findOne(this._userColllection, { _id: new ObjectId(id) });
|
||||
if (!data)
|
||||
return null;
|
||||
|
||||
@ -85,7 +85,7 @@ class UserDatabase extends AbstractUserDatabase {
|
||||
if (!force && this.cache.has(id))
|
||||
return this.cache.get(id);
|
||||
|
||||
const data = await this.db.findOne(this._appCollection, { _id: ObjectId(id) });
|
||||
const data = await this.db.findOne(this._appCollection, { _id: new ObjectId(id) });
|
||||
if (!data)
|
||||
return null;
|
||||
|
||||
@ -238,11 +238,11 @@ class UserDatabase extends AbstractUserDatabase {
|
||||
return false;
|
||||
if (result.created + result.validFor < Date.now()) {
|
||||
// Code existed but is no longer valid
|
||||
await this.db.deleteOne('registrationCodes', { _id: ObjectId(result._id) });
|
||||
await this.db.deleteOne('registrationCodes', { _id: new ObjectId(result._id) });
|
||||
return false;
|
||||
}
|
||||
// Valid code
|
||||
await this.db.deleteOne('registrationCodes', { _id: ObjectId(result._id) });
|
||||
await this.db.deleteOne('registrationCodes', { _id: new ObjectId(result._id) });
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -256,7 +256,7 @@ class UserDatabase extends AbstractUserDatabase {
|
||||
const { json } = user;
|
||||
this.logger.debug(`Updating user ${inspect(json)}`);
|
||||
if (user._id)
|
||||
await this.db.updateOne(this._userColllection, { _id: ObjectId(user._id) }, json);
|
||||
await this.db.updateOne(this._userColllection, { _id: new ObjectId(user._id) }, json);
|
||||
else {
|
||||
const result = await this.db.insertOne(this._userColllection, json);
|
||||
user._id = result.insertedId;
|
||||
@ -267,7 +267,7 @@ class UserDatabase extends AbstractUserDatabase {
|
||||
async updateApplication (app) {
|
||||
const { json } = app;
|
||||
this.logger.debug(`Updating application ${inspect(json)}`);
|
||||
await this.db.updateOne(this._appCollection, { _id: ObjectId(json._id) }, json, true);
|
||||
await this.db.updateOne(this._appCollection, { _id: new ObjectId(json._id) }, json, true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,6 +10,7 @@ const { authenticator } = require('otplib');
|
||||
|
||||
const { inspect } = require('node:util');
|
||||
const path = require('node:path');
|
||||
const fs = require("node:fs");
|
||||
|
||||
// Populated by the constructor
|
||||
const ServiceProfileLinks = {};
|
||||
@ -42,7 +43,7 @@ class UserEndpoint extends ApiEndpoint {
|
||||
|
||||
// Applications
|
||||
[ 'get', '/applications', this.applications.bind(this) ],
|
||||
[ 'post', '/applications', this.createApplication.bind(this) ],
|
||||
[ 'post', '/applications', this.createApplication.bind(this), [ server.auth.createAuthoriser('applications:create', 5) ]],
|
||||
[ 'delete', '/applications/:id', this.deleteApplication.bind(this) ],
|
||||
];
|
||||
|
||||
@ -70,9 +71,14 @@ class UserEndpoint extends ApiEndpoint {
|
||||
|
||||
const x = file.name.split('.');
|
||||
const fileName = `${user.id}.${x[x.length - 1]}`;
|
||||
const filesDir = path.resolve(this.server.serveFiles, 'avatars', fileName);
|
||||
const avatarsDir = path.resolve(this.server.serveFiles, 'avatars');
|
||||
const oldAvatar = path.join(avatarsDir, user.avatar);
|
||||
|
||||
file.mv(filesDir, async (err) => {
|
||||
// Delete old avatar
|
||||
if (fs.statSync(oldAvatar))
|
||||
fs.unlinkSync(oldAvatar);
|
||||
|
||||
file.mv(path.join(avatarsDir, fileName), async (err) => {
|
||||
if (err) {
|
||||
this.logger.error(`Error saving file: ${err.stack}`);
|
||||
return res.status(500).end();
|
||||
@ -235,6 +241,7 @@ class UserEndpoint extends ApiEndpoint {
|
||||
async createApplication (req, res) {
|
||||
const { body, user } = req;
|
||||
const { name, ...opts } = body;
|
||||
this.logger.info(`User ${user.username} (${user.id}) is creating an application`);
|
||||
if (!name)
|
||||
return res.status(400).send('Missing name');
|
||||
const application = await user.createApplication(name, opts);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* eslint-disable camelcase */
|
||||
const { AbstractUserDatabase } = require('../interfaces');
|
||||
const { Util } = require('../../util');
|
||||
const { Util, PermissionManager } = require('../../util');
|
||||
|
||||
const session = require('express-session');
|
||||
const MongoStore = require('connect-mongo');
|
||||
@ -177,6 +177,8 @@ class Authenticator {
|
||||
*/
|
||||
createAuthoriser (permission, level = 1) {
|
||||
|
||||
PermissionManager.ensurePermission(permission);
|
||||
|
||||
const func = async (req, res, next) => {
|
||||
const { user } = req;
|
||||
// Request does not have a user bound to it, response already sent from #_authenticate
|
||||
@ -185,7 +187,7 @@ class Authenticator {
|
||||
// Has permission
|
||||
if (user.hasPermission(permission, level))
|
||||
return next();
|
||||
return res.status(403).send('Access denied');
|
||||
return res.status(403).send('Insufficient permissions');
|
||||
};
|
||||
|
||||
return func.bind(this);
|
||||
|
@ -1,6 +1,6 @@
|
||||
const Argon2 = require('argon2');
|
||||
const { ObjectId } = require('mongodb');
|
||||
const { Util } = require('../../util');
|
||||
const { Util, PermissionManager } = require('../../util');
|
||||
const UserApplicataion = require('./UserApplication');
|
||||
|
||||
// Fields omitted in safeJson
|
||||
@ -9,23 +9,6 @@ const ProtectedFields = [ '_id', 'otpSecret', 'password' ];
|
||||
|
||||
class User {
|
||||
|
||||
static defaultPermissions = {
|
||||
developer: {
|
||||
default: 0
|
||||
},
|
||||
administrator: {
|
||||
default: 0
|
||||
},
|
||||
test: {
|
||||
default: 0,
|
||||
dingus: {
|
||||
bingus: {
|
||||
default: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static validTypes = [];
|
||||
|
||||
constructor (db, data) {
|
||||
@ -50,27 +33,11 @@ class User {
|
||||
throw new Error('Invalid user type');
|
||||
this.type = data.type;
|
||||
|
||||
// TODO: this
|
||||
this.applications = {};
|
||||
this._applications = data.applications || [];
|
||||
// Object.defineProperty(this.applications, 'json', {
|
||||
// get: () => {
|
||||
// const keys = Object.keys(this.applications);
|
||||
// const obj = [];
|
||||
// for (const key of keys) {
|
||||
// obj[key] = this.applications[key].json;
|
||||
// }
|
||||
// return obj;
|
||||
// }
|
||||
// });
|
||||
// const apps = Object.keys(data.applications);
|
||||
// for (const id of apps) {
|
||||
// const app = data.applications[id];
|
||||
// this.applications[id] = new UserApplicataion(this, app);
|
||||
// }
|
||||
|
||||
this.externalProfiles = data.externalProfiles || {};
|
||||
this.permissions = { ...User.defaultPermissions, ...data.permissions };
|
||||
this.permissions = PermissionManager.merge(data.permissions, PermissionManager.DefaultPermissions);
|
||||
|
||||
/** @private */
|
||||
this._passwordHash = data.password || null;
|
||||
@ -169,7 +136,7 @@ class User {
|
||||
* @memberof User
|
||||
*/
|
||||
hasPermission (perm, level = 1) {
|
||||
return Util.checkPermissions(this.permissions, perm, level);
|
||||
return PermissionManager.checkPermissions(this.permissions, perm, level);
|
||||
}
|
||||
|
||||
hasExternalProfile (name) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
const { Util } = require("../../util");
|
||||
const { PermissionManager } = require("../../util");
|
||||
|
||||
// Fields omitted in safeJson
|
||||
// Should be keys used in the json, not the ones defined in the constructor
|
||||
@ -36,7 +36,7 @@ class UserApplicataion {
|
||||
}
|
||||
|
||||
hasPermission (perm, level = 1) {
|
||||
return Util.checkPermissions(this.permissions, perm, level);
|
||||
return PermissionManager.checkPermissions(this.permissions, perm, level);
|
||||
}
|
||||
|
||||
get json () {
|
||||
|
68
src/util/PermissionManager.js
Normal file
68
src/util/PermissionManager.js
Normal file
@ -0,0 +1,68 @@
|
||||
class PermissionManager {
|
||||
|
||||
static DefaultPermissions = {};
|
||||
|
||||
constructor () {
|
||||
throw new Error('This is a static class');
|
||||
}
|
||||
|
||||
static ensurePermission (permission) {
|
||||
|
||||
if (!permission)
|
||||
throw new Error('Missing permission');
|
||||
|
||||
const perms = permission.split(':');
|
||||
let result = 0;
|
||||
for (let i = perms.length - 1; i >= 0; i--) {
|
||||
const perm = perms[i];
|
||||
result = { [perm]: result };
|
||||
}
|
||||
|
||||
PermissionManager.merge(PermissionManager.DefaultPermissions, result);
|
||||
|
||||
}
|
||||
|
||||
static checkPermissions (perms, perm, level = 1) {
|
||||
|
||||
if (!perms || typeof perms !== 'object')
|
||||
throw new Error('Missing perms object');
|
||||
if (!perm || typeof perm !== 'string')
|
||||
throw new Error('Missing perm string');
|
||||
|
||||
// Allow for checking of nested permissions, e.g. administrator:createUser
|
||||
const resolveables = perm.split(':');
|
||||
let selector = null;
|
||||
|
||||
// Iterate through nestedd permissions
|
||||
for (const permission of resolveables) {
|
||||
selector = perms[permission] || null;
|
||||
if (selector === null)
|
||||
return false;
|
||||
if (typeof selector === 'number' && selector >= level || selector.default >= level)
|
||||
return true;
|
||||
perms = selector; // selector is a nested object and may contain the next permission
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
static merge (to, from) {
|
||||
const keys = Object.keys(from);
|
||||
for (const key of keys) {
|
||||
if (typeof to[key] === 'object') {
|
||||
PermissionManager.merge(to[key], from[key]);
|
||||
} else if (typeof from[key] === 'object') {
|
||||
to[key] = { default: to[key] || 0 };
|
||||
PermissionManager.merge(to[key], from[key]);
|
||||
// eslint-disable-next-line no-undefined
|
||||
} else if (to[key] === undefined) {
|
||||
to[key] = from[key];
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = PermissionManager;
|
@ -81,32 +81,6 @@ class Util {
|
||||
|
||||
}
|
||||
|
||||
static checkPermissions (perms, perm, level = 1) {
|
||||
|
||||
if (!perms || typeof perms !== 'object')
|
||||
throw new Error('Missing perms object');
|
||||
if (!perm || typeof perm !== 'string')
|
||||
throw new Error('Missing perm string');
|
||||
|
||||
// Allow for checking of nested permissions, e.g. administrator:createUser
|
||||
const resolveables = perm.split(':');
|
||||
let selector = null;
|
||||
// Iterate through nestedd permissions
|
||||
for (const permission of resolveables) {
|
||||
selector = perms[permission] || null;
|
||||
if (selector === null)
|
||||
return false;
|
||||
else if (typeof selector === 'number' && selector >= level)
|
||||
return true;
|
||||
perms = selector; // selector is a nested object and may contain the next permission
|
||||
}
|
||||
// If the last selector is a nested permission group, check if the group has a default level
|
||||
if (typeof selector === 'object' && selector.default >= level)
|
||||
return true;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* NOT CRYPTOGRAPHICALLY SAFE
|
||||
*
|
||||
|
@ -1,3 +1,4 @@
|
||||
module.exports = {
|
||||
Util: require('./Util')
|
||||
Util: require('./Util'),
|
||||
PermissionManager: require('./PermissionManager')
|
||||
};
|
17
test/PermissionManager.test.js
Normal file
17
test/PermissionManager.test.js
Normal file
@ -0,0 +1,17 @@
|
||||
/* eslint-disable no-console */
|
||||
const { PermissionManager } = require('../src/util');
|
||||
const { inspect } = require('node:util');
|
||||
|
||||
const log = (obj) => {
|
||||
console.log(inspect(obj, { depth: 10 }));
|
||||
};
|
||||
// PermissionManager.ensurePermission('administrator');
|
||||
PermissionManager.ensurePermission('administrator:removeuser:force:bruh:moment:broo');
|
||||
PermissionManager.ensurePermission('developer:toggledebug');
|
||||
PermissionManager.ensurePermission('developer');
|
||||
|
||||
// log(PermissionManager);
|
||||
|
||||
const perms = { developer: { default: 10 }, administrator: 10 };
|
||||
log(PermissionManager.merge(perms, PermissionManager.DefaultPermissions));
|
||||
// log(perms);
|
Loading…
Reference in New Issue
Block a user