Compare commits

...

5 Commits

Author SHA1 Message Date
893aed0945
type checks for mongo wrapper 2022-11-21 12:50:04 +02:00
e2aa998723
endpoint for fetching all users 2022-11-21 12:49:46 +02:00
f0320d1350
correct status code 2022-11-21 12:49:30 +02:00
74ea4d73d6
comments + alias 2022-11-21 12:49:14 +02:00
1218512972
fix typo 2022-11-21 12:48:56 +02:00
6 changed files with 59 additions and 7 deletions

View File

@ -48,11 +48,13 @@ class Server extends EventEmitter {
this._proto = NODE_ENV === 'development' ? 'http' : 'https';
if (!httpOpts?.port) return Util.fatal(new Error(`Missing http.port in server options`));
this.port = httpOpts.port + this._shardId;
// Port number is automatically incremented based on shard #
this.port = httpOpts.port + this._shardId;
// Primarily used by the OAuth methods for the callback url
this.domain = NODE_ENV === 'development' ? `localhost:${this.port}` : options.domain;
this.serveFiles = null;
this.registrationEnabled = options.registrationEnabled;
this.serveFiles = null; // Holds a reference to the directory from which to serve content
if (options.serveFiles) this.serveFiles = path.resolve(options.serveFiles);
this.registrationEnabled = options.registrationEnabled;
this.server = null;
this.app = express();
@ -61,11 +63,14 @@ class Server extends EventEmitter {
this.registry = new Registry(this, { path: path.join(__dirname, 'endpoints') });
this.logger = new LoggerClient({ ...options.logger, name: this.constructor.name });
// Mariadb isn't strictly necessart here for anything, it's just here pre-emptively
// Mariadb isn't strictly necessary here for anything, it's just here pre-emptively
this.mariadb = new MariaDB(this, { options: databases.mariadb, MARIA_HOST, MARIA_USER, MARIA_PORT, MARIA_PASS, MARIA_DB });
// Mongo is used for session and user storage
this.mongodb = new MongoDB(this, { options: databases.mongodb, MONGO_HOST, MONGO_USER, MONGO_PORT, MONGO_PASS, MONGO_DB });
this.userDatabase = new UserDatabase(this, this.mongodb, { validUserTypes });
// Alias
this.users = this.userDatabase;
// Authenticator takes care of sessions, logins and authorisations
this.authenticator = new Authenticator(this, this.userDatabase, {
mongo: this.mongodb,
@ -92,7 +97,7 @@ class Server extends EventEmitter {
'style-src': [ "'self'", "'unsafe-inline'" ],
'img-src': [ "'self'", "https:" ],
'media-src': [ "'self'" ],
'frame-src': [ "youtube.com", "www.youtube.com" ]
'frame-src': [ "'self'" ]
}
},
crossOriginResourcePolicy: {

View File

@ -31,6 +31,21 @@ class UserDatabase extends AbstractUserDatabase {
// this.userCollection = this.db.collection(this._userColllection);
}
async fetchUsers (page, amount = 10, query = {}) {
if (!page) throw new Error('Missing page number');
const data = await this.db.find('users', query, { limit: amount, skip: (page - 1) * amount });
const users = [];
for (const user of data) {
let u = this.cache.get(user._id);
if (!u) {
u = this._createUser(user);
this.cache.set(u.id, u);
}
users.push(u);
}
return users;
}
async fetchUser (id, force = false) {
if (!id) throw new Error('Missing token');

View File

@ -70,6 +70,7 @@ class MongoDB {
*/
async find (db, query, options) {
if (typeof db !== 'string') throw new TypeError('Expecting collection name for the first argument');
this.logger.debug(`Incoming find query for ${db} with parameters ${inspect(query)}`);
const cursor = this.db.collection(db).find(query, options);
@ -87,6 +88,7 @@ class MongoDB {
*/
async findOne (db, query, options = {}) {
if (typeof db !== 'string') throw new TypeError('Expecting collection name for the first argument');
this.logger.debug(`Incoming findOne query for ${db} with parameters ${inspect(query)}`);
const result = await this.db.collection(db).findOne(query, options);
return result;
@ -104,6 +106,7 @@ class MongoDB {
*/
async updateMany (db, filter, data, upsert = false) {
if (typeof db !== 'string') throw new TypeError('Expecting collection name for the first argument');
this.logger.debug(`Incoming update query for '${db}' with parameters\n${inspect(filter)}\nand data\n${inspect(data)}`);
if (!filter) throw new Error(`Cannot run update many without a filter, if you mean to update every single document, pass an empty object`);
const result = await this.db.collection(db).updateMany(filter, { $set: data }, { upsert });
@ -122,6 +125,7 @@ class MongoDB {
*/
async updateOne (db, filter, data, upsert = false) {
if (typeof db !== 'string') throw new TypeError('Expecting collection name for the first argument');
this.logger.debug(`Incoming updateOne query for ${db} with parameters ${inspect(filter)}`);
const result = await this.db.collection(db).updateOne(filter, { $set: data }, { upsert });
return result;
@ -139,6 +143,7 @@ class MongoDB {
*/
async insertOne (db, data) {
if (typeof db !== 'string') throw new TypeError('Expecting collection name for the first argument');
this.logger.debug(`Incoming insertOne query for ${db} with parameters ${inspect(data)}`);
const result = await this.db.collection(db).insertOne(data);
return result;
@ -157,6 +162,7 @@ class MongoDB {
*/
async push (db, filter, data, upsert = false) {
if (typeof db !== 'string') throw new TypeError('Expecting collection name for the first argument');
this.logger.debug(`Incoming push query for ${db}, with upsert ${upsert} and with parameters ${inspect(filter)} and data ${inspect(data)}`);
const result = await this.db.collection(db).updateOne(filter, { $push: data }, { upsert });
return result;
@ -174,6 +180,7 @@ class MongoDB {
*/
random (db, filter = {}, amount = 1) {
if (typeof db !== 'string') throw new TypeError('Expecting collection name for the first argument');
this.logger.debug(`Incoming random query for ${db} with parameters ${inspect(filter)} and amount ${amount}`);
if (amount > 100) amount = 100;

View File

@ -36,7 +36,7 @@ class LoginEndpoint extends ApiEndpoint {
if (!user._otpSecret) return res.status(400).send('2FA not initialised');
if (!user._2fa) return res.status(400).send('2FA not enabled');
if (!authenticator.check(code, user._otpSecret)) return res.status(400).send('Wrong code');
if (!authenticator.check(code, user._otpSecret)) return res.status(401).send('Wrong code');
session.verified = true;
}

View File

@ -0,0 +1,25 @@
const { ApiEndpoint } = require("../../interfaces");
class UsersEndpoint extends ApiEndpoint {
constructor (server) {
super(server, {
name: 'users',
path: '/users'
});
this.methods.push([ 'get', this.getUsers.bind(this), [ server.auth.createAuthoriser('administrator', 10) ]]);
}
async getUsers (req, res) {
const { query } = req;
const { amount, page } = query;
if (!page) return res.status(400).send('Missing page number');
const users = await this.server.users.fetchUsers(page, amount);
res.json(users.map(user => user.json));
}
}
module.exports = UsersEndpoint;

View File

@ -20,7 +20,7 @@ class Util {
static checkPermissions (perms, perm, level = 1) {
if (!perms || typeof perms !== 'object') throw new Error('Missing perms object');
if (!perm || typeof perms !== 'string') throw new Error('Missing perm string');
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(':');