mirror of
https://github.com/Foltik/Shimapan
synced 2025-01-07 08:42:49 -05:00
Redo invite API routes and add full testing suite
This commit is contained in:
parent
2e268c83cb
commit
d6aa85ae80
84
app/routes/invite.js
Normal file
84
app/routes/invite.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
const ModelPath = '../models/';
|
||||||
|
const Invite = require(ModelPath + 'Invite.js');
|
||||||
|
const User = require(ModelPath + 'User.js');
|
||||||
|
|
||||||
|
const wrap = require('../util/wrap.js');
|
||||||
|
const requireAuth = require('../util/requireAuth');
|
||||||
|
const verifyScope = require('../util/verifyScope');
|
||||||
|
const verifyBody = require('../util/verifyBody');
|
||||||
|
|
||||||
|
|
||||||
|
const updateInviteCount = async (req, next) =>
|
||||||
|
User.updateOne({username: req.username}, {$inc: {inviteCount: 1}}).catch(next);
|
||||||
|
|
||||||
|
const verifyUserHasScope = userScope =>
|
||||||
|
scope => verifyScope(userScope, scope);
|
||||||
|
|
||||||
|
const createParams = [{name: 'scope', instance: Array}];
|
||||||
|
router.post('/create', requireAuth('invite.create'), verifyBody(createParams), wrap(async (req, res, next) => {
|
||||||
|
const scope = req.body.scope;
|
||||||
|
const hasPermission = scope.every(verifyUserHasScope(req.scope));
|
||||||
|
if (!hasPermission)
|
||||||
|
return res.status(403).json({message: 'Requested scope exceeds own scope.'});
|
||||||
|
|
||||||
|
const invite = {
|
||||||
|
code: crypto.randomBytes(12).toString('hex'),
|
||||||
|
scope: scope,
|
||||||
|
issuer: req.username,
|
||||||
|
issued: Date.now(),
|
||||||
|
expires: req.body.expires
|
||||||
|
};
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
Invite.create(invite).catch(next),
|
||||||
|
updateInviteCount(req, next)
|
||||||
|
]);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Invite created.',
|
||||||
|
code: invite.code
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
const deleteParams = [{name: 'code', type: 'string'}];
|
||||||
|
router.post('/delete', requireAuth('invite.delete'), verifyBody(deleteParams), wrap(async (req, res, next) => {
|
||||||
|
let query = {code: req.body.code};
|
||||||
|
|
||||||
|
// Users need a permission to delete invites other than their own
|
||||||
|
if (!verifyScope(req.scope, 'invite.delete.others'))
|
||||||
|
query.issuer = req.username;
|
||||||
|
|
||||||
|
// Find the invite
|
||||||
|
const invite = await Invite.findOne(query).catch(next);
|
||||||
|
if (!invite)
|
||||||
|
return res.status(404).json({message: 'Invite not found.'});
|
||||||
|
|
||||||
|
// Users need a permission to delete invites that have been used
|
||||||
|
if (!verifyScope(req.scope, 'invite.delete.used') && invite.used != null && invite.recipient != null)
|
||||||
|
return res.status(403).json({message: 'Forbidden to delete used invites.'});
|
||||||
|
|
||||||
|
await Invite.deleteOne({_id: invite._id}).catch(next);
|
||||||
|
res.status(200).json({message: 'Invite deleted.'});
|
||||||
|
}));
|
||||||
|
|
||||||
|
const getParams = [{name: 'code', type: 'string', optional: true}];
|
||||||
|
router.get('/get', requireAuth('invite.get'), verifyBody(getParams), wrap(async (req, res, next) => {
|
||||||
|
let query = {};
|
||||||
|
|
||||||
|
// Users need a permission to list invites other than their own
|
||||||
|
if (!verifyScope(req.scope, 'invite.get.others'))
|
||||||
|
query.issuer = req.username;
|
||||||
|
|
||||||
|
// Narrow down the query by code if specified
|
||||||
|
if (req.body.code)
|
||||||
|
query.code = req.body.code;
|
||||||
|
|
||||||
|
const invites = await Invite.find(query).catch(next);
|
||||||
|
res.status(200).json(invites);
|
||||||
|
}));
|
||||||
|
|
||||||
|
module.exports = router;
|
@ -1,78 +0,0 @@
|
|||||||
var express = require('express');
|
|
||||||
var router = express.Router();
|
|
||||||
|
|
||||||
var Invite = require('../models/Invite.js');
|
|
||||||
|
|
||||||
var requireScope = function (perm) {
|
|
||||||
return function(req, res, next) {
|
|
||||||
User.findOne({username: req.session.passport.user}, function(err, user) {
|
|
||||||
if (err) throw err;
|
|
||||||
if (user.scope.indexOf(perm) === -1)
|
|
||||||
res.status(400).json({'message': 'No permission.'});
|
|
||||||
else
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
router.post('/create', function (req, res) {
|
|
||||||
if (!req.body.scope) {
|
|
||||||
res.status(400).json({'message': 'Bad request.'});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var scope;
|
|
||||||
try {
|
|
||||||
scope = JSON.parse(req.body.scope);
|
|
||||||
} catch (e) {
|
|
||||||
res.status(500).json({'message': e.name + ': ' + e.message});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var expiry = req.body.exp;
|
|
||||||
if (!expiry || expiry < Date.now())
|
|
||||||
expiry = 0;
|
|
||||||
|
|
||||||
var entry = {
|
|
||||||
code: crypto.randomBytes(12).toString('hex'),
|
|
||||||
scope: scope,
|
|
||||||
issuer: req.session.passport.user,
|
|
||||||
issued: Date.now(),
|
|
||||||
exp: expiry
|
|
||||||
};
|
|
||||||
|
|
||||||
Invite.create(entry, function (err) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
} else {
|
|
||||||
res.status(200).json({
|
|
||||||
code: entry.code,
|
|
||||||
scope: entry.scope
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/get', function (req, res, next) {
|
|
||||||
var query = {issuer: req.session.passport.user};
|
|
||||||
|
|
||||||
if (req.body.code)
|
|
||||||
query.code = req.body.code;
|
|
||||||
|
|
||||||
Invite.find(query, function (err, invites) {
|
|
||||||
if (err) {
|
|
||||||
next(err);
|
|
||||||
} else {
|
|
||||||
res.status(200).json(invites);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/delete', function (req, res, next) {
|
|
||||||
Invite.deleteOne({code: req.body.code}, function (err) {
|
|
||||||
if (err) next(err);
|
|
||||||
else res.status(200).json({'message': 'Successfully deleted.'});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
|
287
test/api.js
287
test/api.js
@ -65,7 +65,7 @@ describe('Authentication', function() {
|
|||||||
async function verifyRejectedInvite(invite, message) {
|
async function verifyRejectedInvite(invite, message) {
|
||||||
const user = {displayname: 'user', password: 'pass', invite: 'code'};
|
const user = {displayname: 'user', password: 'pass', invite: 'code'};
|
||||||
if (invite) {
|
if (invite) {
|
||||||
await util.createInvite(invite, agent);
|
await util.insertInvite(invite, agent);
|
||||||
user.invite = invite.code;
|
user.invite = invite.code;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,15 +196,12 @@ describe('Uploading', () => {
|
|||||||
beforeEach(async () => util.clearDatabase());
|
beforeEach(async () => util.clearDatabase());
|
||||||
|
|
||||||
describe('/POST upload', () => {
|
describe('/POST upload', () => {
|
||||||
async function verifySuccessfulUpload(file, username) {
|
async function verifySuccessfulUpload(file, key) {
|
||||||
// Get file stats beforehand
|
// Get file stats beforehand
|
||||||
const [fileHash, fileSize] = await Promise.all([util.fileHash(file), util.fileSize(file)]);
|
const [fileHash, fileSize] = await Promise.all([util.fileHash(file), util.fileSize(file)]);
|
||||||
|
|
||||||
// Get the user stats beforehand
|
|
||||||
const userBefore = await User.findOne({username: username}, {_id: 0, uploadCount: 1, uploadSize: 1});
|
|
||||||
|
|
||||||
// Submit the upload and verify the result
|
// Submit the upload and verify the result
|
||||||
const res = await util.upload(file, agent);
|
const res = await util.upload(file, agent, key);
|
||||||
res.should.have.status(200);
|
res.should.have.status(200);
|
||||||
res.body.should.be.a('object');
|
res.body.should.be.a('object');
|
||||||
res.body.should.have.property('url');
|
res.body.should.have.property('url');
|
||||||
@ -221,21 +218,38 @@ describe('Uploading', () => {
|
|||||||
uploadHash.should.equal(fileHash);
|
uploadHash.should.equal(fileHash);
|
||||||
uploadSize.should.equal(fileSize);
|
uploadSize.should.equal(fileSize);
|
||||||
|
|
||||||
|
return fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifySuccessfulUserUpload(file, username) {
|
||||||
|
// Get the user's stats beforehand
|
||||||
|
const userBefore = await User.findOne({username: username}, {_id: 0, uploadCount: 1, uploadSize: 1});
|
||||||
|
|
||||||
|
const fileSize = await verifySuccessfulUpload(file);
|
||||||
|
|
||||||
// Verify the user's stats have been updated correctly
|
// Verify the user's stats have been updated correctly
|
||||||
const userAfter = await User.findOne({username: username}, {_id: 0, uploadCount: 1, uploadSize: 1});
|
const userAfter = await User.findOne({username: username}, {_id: 0, uploadCount: 1, uploadSize: 1});
|
||||||
userAfter.uploadCount.should.equal(userBefore.uploadCount + 1);
|
userAfter.uploadCount.should.equal(userBefore.uploadCount + 1);
|
||||||
userAfter.uploadSize.should.equal(userBefore.uploadSize + fileSize);
|
userAfter.uploadSize.should.equal(userBefore.uploadSize + fileSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function verifySuccessfulKeyUpload(key, file) {
|
async function verifySuccessfulKeyUpload(file, key) {
|
||||||
|
// Get the key's stats beforehand
|
||||||
|
const keyBefore = await Key.findOne({key: key}, {_id: 0, uploadCount: 1, uploadSize: 1});
|
||||||
|
|
||||||
|
const fileSize = await verifySuccessfulUpload(file, key);
|
||||||
|
|
||||||
|
// Verify the key's stats have been updated correctly
|
||||||
|
const keyAfter = await Key.findOne({key: key}, {_id: 0, uploadCount: 1, uploadSize: 1});
|
||||||
|
keyAfter.uploadCount.should.equal(keyBefore.uploadCount + 1);
|
||||||
|
keyAfter.uploadSize.should.equal(keyBefore.uploadSize + fileSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function verifyFailedUpload(file, status, message) {
|
async function verifyFailedUpload(file, status, message, key) {
|
||||||
const fileCountBefore = await util.directoryFileCount(config.get('Upload.path'));
|
const fileCountBefore = await util.directoryFileCount(config.get('Upload.path'));
|
||||||
const uploadCountBefore = await Upload.countDocuments({});
|
const uploadCountBefore = await Upload.countDocuments({});
|
||||||
|
|
||||||
const res = await util.upload(file, agent);
|
const res = await util.upload(file, agent, key);
|
||||||
res.should.have.status(status);
|
res.should.have.status(status);
|
||||||
res.body.should.be.a('object');
|
res.body.should.be.a('object');
|
||||||
res.body.should.have.property('message').equal(message);
|
res.body.should.have.property('message').equal(message);
|
||||||
@ -248,28 +262,43 @@ describe('Uploading', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('0 Valid Request', () => {
|
describe('0 Valid Request', () => {
|
||||||
it('SHOULD accept logged in valid upload', async () => {
|
it('SHOULD accept an upload from a valid session', async () => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
util.createTestSession(agent),
|
util.createTestSession(agent),
|
||||||
util.createTestFile(2048, 'test.bin')
|
util.createTestFile(2048, 'test.bin')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await verifySuccessfulUpload('test.bin', 'user');
|
await verifySuccessfulUserUpload('test.bin', 'user');
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
util.logout(agent),
|
util.logout(agent),
|
||||||
util.deleteFile('test.bin')
|
util.deleteFile('test.bin')
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('SHOULD accept an upload from a valid api key', async () => {
|
||||||
|
await Promise.all([
|
||||||
|
util.createTestKey(['file.upload']),
|
||||||
|
util.createTestFile(2048, 'test.bin')
|
||||||
|
]);
|
||||||
|
|
||||||
|
await verifySuccessfulKeyUpload('test.bin', 'key');
|
||||||
|
|
||||||
|
return util.deleteFile('test.bin');
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('1 Invalid Authentication', () => {
|
describe('1 Invalid Authentication', () => {
|
||||||
it('SHOULD NOT accept an unauthenticated request', async () =>
|
it('SHOULD NOT accept an unauthenticated request', async () => {
|
||||||
verifyFailedUpload(null, 401, 'Unauthorized.')
|
await util.createTestFile(2048, 'test.bin');
|
||||||
);
|
|
||||||
|
|
||||||
it('SHOULD NOT accept a request without file.upload scope', async () => {
|
await verifyFailedUpload(null, 401, 'Unauthorized.');
|
||||||
await util.createInvite({code: 'code', scope: [], issuer: 'Mocha'});
|
|
||||||
|
return util.deleteFile('test.bin');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('SHOULD NOT accept a session request without file.upload scope', async () => {
|
||||||
|
await util.insertInvite({code: 'code', scope: [], issuer: 'Mocha'});
|
||||||
await util.registerUser({displayname: 'user', password: 'pass', invite: 'code'}, agent);
|
await util.registerUser({displayname: 'user', password: 'pass', invite: 'code'}, agent);
|
||||||
await util.login({displayname: 'user', password: 'pass'}, agent);
|
await util.login({displayname: 'user', password: 'pass'}, agent);
|
||||||
|
|
||||||
@ -282,6 +311,17 @@ describe('Uploading', () => {
|
|||||||
util.deleteFile('test.bin')
|
util.deleteFile('test.bin')
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('SHOULD NOT accept a key request without file.upload scope', async () => {
|
||||||
|
await Promise.all([
|
||||||
|
util.createTestKey([]),
|
||||||
|
util.createTestFile(2048, 'test.bin')
|
||||||
|
]);
|
||||||
|
|
||||||
|
await verifyFailedUpload('test.bin', 403, 'Forbidden.', 'key');
|
||||||
|
|
||||||
|
return util.deleteFile('test.bin');
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('3 Invalid File', () => {
|
describe('3 Invalid File', () => {
|
||||||
@ -306,9 +346,222 @@ describe('Uploading', () => {
|
|||||||
await verifyFailedUpload(null, 400, 'No file specified.');
|
await verifyFailedUpload(null, 400, 'No file specified.');
|
||||||
|
|
||||||
return util.logout(agent);
|
return util.logout(agent);
|
||||||
})
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Invites', () => {
|
||||||
|
beforeEach(async () => util.clearDatabase());
|
||||||
|
|
||||||
|
async function verifyCreatedInvite(invite) {
|
||||||
|
const res = await util.createInvite(invite, agent);
|
||||||
|
util.verifyResponse(res, 200, 'Invite created.');
|
||||||
|
res.body.should.have.property('code').match(/^[A-Fa-f0-9]+$/);
|
||||||
|
|
||||||
|
const dbInvite = await Invite.findOne({code: res.body.code});
|
||||||
|
dbInvite.should.not.equal(null);
|
||||||
|
dbInvite.scope.should.deep.equal(invite.scope);
|
||||||
|
dbInvite.issuer.should.equal('user');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyDeletedInvite(code) {
|
||||||
|
const res = await util.deleteInvite(code, agent);
|
||||||
|
util.verifyResponse(res, 200, 'Invite deleted.');
|
||||||
|
|
||||||
|
const inviteCount = await Invite.countDocuments({code: code});
|
||||||
|
inviteCount.should.equal(0, 'The invite should have been removed from the database');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyInviteSearch(codes) {
|
||||||
|
const res = await util.getInvites({}, agent);
|
||||||
|
res.should.have.status(200);
|
||||||
|
res.body.should.be.a('Array');
|
||||||
|
|
||||||
|
codes.sort();
|
||||||
|
const resCodes = res.body.map(invite => invite.code).sort();
|
||||||
|
|
||||||
|
resCodes.should.deep.equal(codes, 'All invites should be present in result');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifySingleSearch(code) {
|
||||||
|
const res = await util.getInvites({code: code}, agent);
|
||||||
|
res.should.have.status(200);
|
||||||
|
res.body.should.be.a('Array');
|
||||||
|
res.body.should.have.length(1);
|
||||||
|
res.body[0].code.should.equal(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('/POST create', () => {
|
||||||
|
describe('0 Valid Request', () => {
|
||||||
|
it('SHOULD create an invite with valid scope from a valid session', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create', 'file.upload']);
|
||||||
|
return verifyCreatedInvite({scope: ['file.upload']});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('1 Invalid Scope', () => {
|
||||||
|
it('SHOULD NOT create in invite without invite.create scope', async () => {
|
||||||
|
await util.createSession(agent, ['file.upload']);
|
||||||
|
const res = await util.createInvite({scope: ['file.upload']}, agent);
|
||||||
|
util.verifyResponse(res, 403, 'Forbidden.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('SHOULD NOT create an invite with a scope exceeding the requesters', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create', 'file.upload']);
|
||||||
|
const res = await util.createInvite({scope: ['user.ban']}, agent);
|
||||||
|
util.verifyResponse(res, 403, 'Requested scope exceeds own scope.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('2 Malformed Request', () => {
|
||||||
|
it('SHOULD return an error when scope is not specified.', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create']);
|
||||||
|
const res = await util.createInvite(null, agent);
|
||||||
|
util.verifyResponse(res, 400, 'scope not specified.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('SHOULD return an error when scope is not an array', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create']);
|
||||||
|
const res = await util.createInvite({scope: {broken: 'object'}}, agent);
|
||||||
|
util.verifyResponse(res, 400, 'scope malformed.');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('/POST delete', () => {
|
||||||
|
describe('0 Valid Request', () => {
|
||||||
|
it('SHOULD delete an invite with valid permission from a valid session', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create', 'invite.delete', 'file.upload']);
|
||||||
|
const res = await util.createInvite({scope: ['file.upload']}, agent);
|
||||||
|
return verifyDeletedInvite(res.body.code);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('SHOULD delete another users invite with invite.delete.others scope', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create', 'file.upload'], 'alice');
|
||||||
|
const invite = await util.createInvite({scope: ['file.upload']}, agent);
|
||||||
|
await util.logout(agent);
|
||||||
|
|
||||||
|
await util.createSession(agent, ['invite.create', 'invite.delete', 'invite.delete.others'], 'eve');
|
||||||
|
return verifyDeletedInvite(invite.body.code);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('SHOULD delete a usedinvite with invite.delete.used scope', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create', 'invite.delete', 'invite.delete.used', 'file.upload'], 'alice');
|
||||||
|
const invite = await util.createInvite({scope: ['file.upload']}, agent);
|
||||||
|
await util.registerUser({displayname: 'bob', password: 'hunter2', invite: invite.body.code}, agent);
|
||||||
|
|
||||||
|
return verifyDeletedInvite(invite.body.code);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('1 Invalid Scope', () => {
|
||||||
|
it('SHOULD NOT delete an invite without invite.delete scope', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create', 'file.upload']);
|
||||||
|
const invite = await util.createInvite({scope: ['file.upload']}, agent);
|
||||||
|
const res = await util.deleteInvite(invite.body.code, agent);
|
||||||
|
util.verifyResponse(res, 403, 'Forbidden.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('SHOULD NOT delete another users invite without invite.delete.others scope', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create', 'file.upload'], 'alice');
|
||||||
|
const invite = await util.createInvite({scope: ['file.upload']}, agent);
|
||||||
|
await util.logout(agent);
|
||||||
|
|
||||||
|
await util.createSession(agent, ['invite.create', 'invite.delete'], 'eve');
|
||||||
|
const res = await util.deleteInvite(invite.body.code, agent);
|
||||||
|
util.verifyResponse(res, 404, 'Invite not found.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('SHOULD NOT delete a used invite without invite.delete.used scope', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create', 'invite.delete', 'file.upload'], 'alice');
|
||||||
|
const invite = await util.createInvite({scope: ['file.upload']}, agent);
|
||||||
|
|
||||||
|
await util.registerUser({displayname: 'bob', password: 'hunter2', invite: invite.body.code}, agent);
|
||||||
|
|
||||||
|
const res = await util.deleteInvite(invite.body.code, agent);
|
||||||
|
util.verifyResponse(res, 403, 'Forbidden to delete used invites.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('2 Invalid Code', () => {
|
||||||
|
it('SHOULD return an error when the invite is not found', async () => {
|
||||||
|
await util.createSession(agent, ['invite.delete']);
|
||||||
|
const res = await util.deleteInvite('bogus', agent);
|
||||||
|
util.verifyResponse(res, 404, 'Invite not found.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('3 Malformed Request', () => {
|
||||||
|
it('SHOULD return an error when no code was specified', async () => {
|
||||||
|
await util.createSession(agent, ['invite.delete']);
|
||||||
|
const res = await util.deleteInvite(null, agent);
|
||||||
|
util.verifyResponse(res, 400, 'code not specified.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('SHOULD return an error when the code is not a string', async () => {
|
||||||
|
await util.createSession(agent, ['invite.delete']);
|
||||||
|
const res = await util.deleteInvite({break: 'everything'}, agent);
|
||||||
|
util.verifyResponse(res, 400, 'code malformed.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('/POST get', () => {
|
||||||
|
describe('0 Valid Request', () => {
|
||||||
|
it('SHOULD get multiple invites from a valid session', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create', 'invite.get', 'file.upload']);
|
||||||
|
const inv1 = await util.createInvite({scope: ['file.upload']}, agent);
|
||||||
|
const inv2 = await util.createInvite({scope: ['invite.create']}, agent);
|
||||||
|
|
||||||
|
return verifyInviteSearch([inv1.body.code, inv2.body.code]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('SHOULD get a single invite from a valid session', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create', 'invite.get', 'file.upload']);
|
||||||
|
const inv = await util.createInvite({scope: ['file.upload']}, agent);
|
||||||
|
|
||||||
|
return verifySingleSearch(inv.body.code);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('SHOULD get another users invite with invite.get.others scope', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create', 'file.upload'], 'alice');
|
||||||
|
const inv = await util.createInvite({scope: ['file.upload']}, agent);
|
||||||
|
await util.logout(agent);
|
||||||
|
|
||||||
|
await util.createSession(agent, ['invite.get', 'invite.get.others'], 'eve');
|
||||||
|
return verifySingleSearch(inv.body.code);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('1 Invalid Scope', () => {
|
||||||
|
it('SHOULD NOT get invites without invite.get scope', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create', 'file.upload']);
|
||||||
|
const res = await util.getInvites({code: 'bogus'}, agent);
|
||||||
|
util.verifyResponse(res, 403, 'Forbidden.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('SHOULD NOT get another users invite without invite.get.others scope', async () => {
|
||||||
|
await util.createSession(agent, ['invite.create', 'file.upload'], 'alice');
|
||||||
|
const invite = await util.createInvite({scope: ['file.upload']}, agent);
|
||||||
|
await util.logout(agent);
|
||||||
|
|
||||||
|
await util.createSession(agent, ['invite.get'], 'eve');
|
||||||
|
const res = await util.getInvites({code: invite.body.code}, agent);
|
||||||
|
res.should.have.status(200);
|
||||||
|
res.body.should.be.a('Array');
|
||||||
|
res.body.should.have.length(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('2 Malformed Request', () => {
|
||||||
|
it('SHOULD return an error when code is not a string', async () => {
|
||||||
|
await util.createSession(agent, ['invite.get']);
|
||||||
|
const res = await util.getInvites({code: {what: 'even'}}, agent);
|
||||||
|
util.verifyResponse(res, 400, 'code malformed.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
after(() => server.close(() => process.exit(0)));
|
after(() => server.close(() => process.exit(0)));
|
||||||
|
@ -14,6 +14,19 @@ const crypto = require('crypto');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const fsPromises = fs.promises;
|
const fsPromises = fs.promises;
|
||||||
|
|
||||||
|
//---------------- RESPONSE VERIFICATION ----------------//
|
||||||
|
|
||||||
|
exports.verifyResponse = (res, status, message) => {
|
||||||
|
res.should.have.status(status);
|
||||||
|
res.body.should.be.a('object');
|
||||||
|
res.body.should.have.property('message').equal(message);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.verifyResponseObj = (res, status, obj) => {
|
||||||
|
res.should.have.status(status);
|
||||||
|
res.body.should.deep.equal(obj);
|
||||||
|
};
|
||||||
|
|
||||||
//---------------- DATABASE UTIL ----------------//
|
//---------------- DATABASE UTIL ----------------//
|
||||||
|
|
||||||
exports.clearDatabase = () =>
|
exports.clearDatabase = () =>
|
||||||
@ -24,44 +37,44 @@ exports.clearDatabase = () =>
|
|||||||
Upload.remove({})
|
Upload.remove({})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
exports.insertInvite = invite =>
|
||||||
|
Invite.create(invite);
|
||||||
|
|
||||||
|
exports.insertKey = key =>
|
||||||
|
Key.create(key);
|
||||||
|
|
||||||
//---------------- API ROUTES ----------------//
|
//---------------- API ROUTES ----------------//
|
||||||
|
|
||||||
exports.login = (credentials, agent) =>
|
exports.login = (credentials, agent) =>
|
||||||
agent
|
agent.post('/api/auth/login')
|
||||||
.post('/api/auth/login')
|
|
||||||
.send(credentials);
|
.send(credentials);
|
||||||
|
|
||||||
exports.logout = agent =>
|
exports.logout = agent =>
|
||||||
agent
|
agent.post('/api/auth/logout');
|
||||||
.post('/api/auth/logout');
|
|
||||||
|
|
||||||
exports.createInvite = (invite) =>
|
|
||||||
Invite.create(invite);
|
|
||||||
|
|
||||||
exports.registerUser = (user, agent) =>
|
exports.registerUser = (user, agent) =>
|
||||||
agent
|
agent.post('/api/auth/register')
|
||||||
.post('/api/auth/register')
|
|
||||||
.send(user);
|
.send(user);
|
||||||
|
|
||||||
exports.whoami = (agent) =>
|
exports.whoami = (agent) =>
|
||||||
agent
|
agent.get('/api/auth/whoami')
|
||||||
.get('/api/auth/whoami')
|
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
//---------------- TEST ENTRY CREATION ----------------//
|
//---------------- TEST ENTRY CREATION ----------------//
|
||||||
|
|
||||||
exports.createTestInvite = () =>
|
exports.createTestInvite = () =>
|
||||||
exports.createInvite({code: 'code', scope: ['file.upload'], issuer: 'Mocha'});
|
exports.insertInvite({code: 'code', scope: ['file.upload'], issuer: 'Mocha'});
|
||||||
|
|
||||||
exports.createTestInvites = (n) =>
|
exports.createTestInvites = (n) =>
|
||||||
Promise.all(
|
Promise.all(
|
||||||
Array.from(new Array(n), (val, index) => 'code' + index)
|
Array.from(new Array(n), (val, index) => 'code' + index)
|
||||||
.map(code => exports.createInvite({code: code, scope: ['file.upload'], issuer: 'Mocha'}))
|
.map(code => exports.insertInvite({code: code, scope: ['file.upload'], issuer: 'Mocha'}))
|
||||||
);
|
);
|
||||||
|
|
||||||
exports.createTestUser = async agent => {
|
exports.createTestUser = async agent => {
|
||||||
await exports.createTestInvite();
|
await exports.createTestInvite();
|
||||||
return exports.registerUser({displayname: 'user', password: 'pass', invite: 'code'}, agent);
|
exports.registerUser({displayname: 'user', password: 'pass', invite: 'code'}, agent);
|
||||||
|
await Invite.deleteOne({code: 'code'});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.createTestSession = async agent => {
|
exports.createTestSession = async agent => {
|
||||||
@ -69,9 +82,19 @@ exports.createTestSession = async agent => {
|
|||||||
return exports.login({displayname: 'user', password: 'pass'}, agent);
|
return exports.login({displayname: 'user', password: 'pass'}, agent);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.createSession = async (agent, scope, displayname) => {
|
||||||
|
await exports.insertInvite({code: 'code', scope: scope, issuer: 'Mocha'});
|
||||||
|
await exports.registerUser({displayname: displayname ? displayname : 'user', password: 'pass', invite: 'code'}, agent);
|
||||||
|
await exports.login({displayname: displayname ? displayname : 'user', password: 'pass'}, agent);
|
||||||
|
await Invite.deleteOne({code: 'code'});
|
||||||
|
};
|
||||||
|
|
||||||
exports.createTestFile = (size, name) =>
|
exports.createTestFile = (size, name) =>
|
||||||
fsPromises.writeFile(name, Buffer.allocUnsafe(size));
|
fsPromises.writeFile(name, Buffer.allocUnsafe(size));
|
||||||
|
|
||||||
|
exports.createTestKey = scope =>
|
||||||
|
exports.insertKey({key: 'key', identifier: 'test', scope: scope, issuer: 'Mocha'});
|
||||||
|
|
||||||
//---------------- FILESYSTEM ----------------//
|
//---------------- FILESYSTEM ----------------//
|
||||||
|
|
||||||
exports.deleteFile = file =>
|
exports.deleteFile = file =>
|
||||||
@ -97,7 +120,25 @@ exports.directoryFileCount = async dir =>
|
|||||||
|
|
||||||
//---------------- UPLOADS ----------------//
|
//---------------- UPLOADS ----------------//
|
||||||
|
|
||||||
exports.upload = (file, agent) =>
|
exports.upload = (file, agent, key) => {
|
||||||
agent
|
const request = agent.post('/api/upload');
|
||||||
.post('/api/upload')
|
|
||||||
.attach('file', file);
|
if (key)
|
||||||
|
request.field('key', key);
|
||||||
|
|
||||||
|
return request.attach('file', file);
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------- Invites ----------------//
|
||||||
|
|
||||||
|
exports.createInvite = (invite, agent) =>
|
||||||
|
agent.post('/api/invites/create')
|
||||||
|
.send(invite);
|
||||||
|
|
||||||
|
exports.deleteInvite = (code, agent) =>
|
||||||
|
agent.post('/api/invites/delete')
|
||||||
|
.send({code: code});
|
||||||
|
|
||||||
|
exports.getInvites = (query, agent) =>
|
||||||
|
agent.get('/api/invites/get')
|
||||||
|
.send(query);
|
||||||
|
Loading…
Reference in New Issue
Block a user