2017-10-13 16:17:18 -04:00
|
|
|
|
process.env.NODE_ENV = 'test';
|
|
|
|
|
|
2018-07-25 21:34:16 -04:00
|
|
|
|
const chai = require('chai');
|
|
|
|
|
chai.use(require('chai-http'));
|
2018-07-26 19:40:42 -04:00
|
|
|
|
const should = chai.should();
|
2018-08-01 16:53:55 -04:00
|
|
|
|
const describe = require('mocha').describe;
|
2018-07-25 21:34:16 -04:00
|
|
|
|
|
2018-07-26 19:01:16 -04:00
|
|
|
|
const ModelPath = '../app/models/';
|
|
|
|
|
const User = require(ModelPath + 'User.js');
|
|
|
|
|
const Upload = require(ModelPath + 'Upload.js');
|
|
|
|
|
const Key = require(ModelPath + 'Key.js');
|
|
|
|
|
const Invite = require(ModelPath + 'Invite.js');
|
2017-10-13 16:17:18 -04:00
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
const util = require('./testUtil.js');
|
|
|
|
|
const canonicalize = require('../app/util/canonicalize').canonicalize;
|
2017-10-13 16:17:18 -04:00
|
|
|
|
|
2018-07-26 17:34:47 -04:00
|
|
|
|
const config = require('config');
|
2018-07-26 13:17:37 -04:00
|
|
|
|
let app;
|
|
|
|
|
let server;
|
|
|
|
|
let agent;
|
|
|
|
|
|
|
|
|
|
before(() => {
|
|
|
|
|
const main = require('../server.js');
|
|
|
|
|
app = main.app;
|
|
|
|
|
server = main.server;
|
|
|
|
|
agent = chai.request.agent(app);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
after(() => {
|
|
|
|
|
server.close();
|
|
|
|
|
});
|
|
|
|
|
|
2018-07-29 11:06:57 -04:00
|
|
|
|
beforeEach(() => util.clearDatabase());
|
2017-10-13 16:17:18 -04:00
|
|
|
|
|
2018-08-01 19:09:40 -04:00
|
|
|
|
describe('Authentication', () => {
|
2018-07-25 18:45:38 -04:00
|
|
|
|
describe('/POST register', () => {
|
|
|
|
|
describe('0 Valid Request', () => {
|
|
|
|
|
async function verifySuccessfulRegister(user) {
|
|
|
|
|
await util.createTestInvite();
|
2018-07-24 19:39:55 -04:00
|
|
|
|
|
2018-07-26 13:17:37 -04:00
|
|
|
|
const res = await util.registerUser(user, agent);
|
2018-07-29 11:06:57 -04:00
|
|
|
|
util.verifyResponse(res, 200, 'Registration successful.');
|
2018-07-24 19:39:55 -04:00
|
|
|
|
|
2018-07-26 19:40:42 -04:00
|
|
|
|
const userCount = await User.countDocuments({displayname: user.displayname});
|
2018-07-29 11:06:57 -04:00
|
|
|
|
userCount.should.equal(1, 'The user should have be created in the database');
|
2018-07-25 18:45:38 -04:00
|
|
|
|
|
2018-07-26 19:40:42 -04:00
|
|
|
|
const inviteCount = await Invite.countDocuments({code: user.invite, recipient: canonicalize(user.displayname)});
|
2018-07-29 11:06:57 -04:00
|
|
|
|
inviteCount.should.equal(1, 'The invite should be marked as used by the user');
|
2018-07-25 18:45:38 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
it('MUST register a valid user with a valid invite', async () =>
|
2018-07-26 19:40:42 -04:00
|
|
|
|
verifySuccessfulRegister({displayname: 'user', password: 'pass', invite: 'code'})
|
2018-07-25 18:45:38 -04:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
it('MUST register a username with unicode symbols and a valid invite', async () =>
|
2018-07-26 19:40:42 -04:00
|
|
|
|
verifySuccessfulRegister({displayname: 'ᴮᴵᴳᴮᴵᴿᴰ', password: 'pass', invite: 'code'})
|
2018-07-25 18:45:38 -04:00
|
|
|
|
);
|
2017-10-13 16:17:18 -04:00
|
|
|
|
});
|
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
describe('1 Invalid Invites', () => {
|
|
|
|
|
async function verifyRejectedInvite(invite, message) {
|
2018-07-26 19:40:42 -04:00
|
|
|
|
const user = {displayname: 'user', password: 'pass', invite: 'code'};
|
2018-07-25 18:45:38 -04:00
|
|
|
|
if (invite) {
|
2018-07-28 17:13:19 -04:00
|
|
|
|
await util.insertInvite(invite, agent);
|
2018-07-25 18:45:38 -04:00
|
|
|
|
user.invite = invite.code;
|
|
|
|
|
}
|
2017-10-13 16:17:18 -04:00
|
|
|
|
|
2018-07-26 13:17:37 -04:00
|
|
|
|
const res = await(util.registerUser(user, agent));
|
2018-07-29 11:06:57 -04:00
|
|
|
|
util.verifyResponse(res, 422, message);
|
2018-07-26 16:54:08 -04:00
|
|
|
|
|
2018-07-26 19:40:42 -04:00
|
|
|
|
const inviteCount = await Invite.countDocuments({code: user.invite, recipient: canonicalize(user.displayname)});
|
2018-07-29 11:06:57 -04:00
|
|
|
|
inviteCount.should.equal(0, 'Invite should not be marked as used or received by the user');
|
2018-07-25 18:45:38 -04:00
|
|
|
|
}
|
2017-10-13 16:17:18 -04:00
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
it('MUST NOT register a nonexistant invite', async () =>
|
|
|
|
|
verifyRejectedInvite(null, 'Invalid invite code.')
|
|
|
|
|
);
|
2018-07-24 19:39:55 -04:00
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
it('MUST NOT register a used invite', async () =>
|
2018-07-26 19:40:42 -04:00
|
|
|
|
verifyRejectedInvite({code: 'code', used: new Date(), issuer: 'Mocha'}, 'Invite already used.')
|
2018-07-25 18:45:38 -04:00
|
|
|
|
);
|
2018-07-24 19:39:55 -04:00
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
it('MUST NOT register an expired invite', async () =>
|
2018-07-27 14:23:23 -04:00
|
|
|
|
verifyRejectedInvite({code: 'code', expires: new Date(), issuer: 'Mocha'}, 'Invite expired.')
|
2018-07-25 18:45:38 -04:00
|
|
|
|
);
|
2017-10-13 16:17:18 -04:00
|
|
|
|
});
|
|
|
|
|
|
2018-07-26 19:40:42 -04:00
|
|
|
|
describe('2 Invalid Displaynames', () => {
|
2018-08-01 16:53:55 -04:00
|
|
|
|
async function verifyRejectedUsername(user, code, message) {
|
2018-07-26 13:17:37 -04:00
|
|
|
|
const res = await util.registerUser(user, agent);
|
2018-08-01 16:53:55 -04:00
|
|
|
|
util.verifyResponse(res, code, message);
|
2018-07-26 16:54:08 -04:00
|
|
|
|
|
2018-07-26 19:40:42 -04:00
|
|
|
|
const inviteCount = await Invite.countDocuments({code: user.invite, recipient: canonicalize(user.displayname)});
|
2018-07-29 11:06:57 -04:00
|
|
|
|
inviteCount.should.equal(0, 'The invite should not be inserted into the database after rejection');
|
2018-07-25 18:45:38 -04:00
|
|
|
|
}
|
2018-07-24 19:39:55 -04:00
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
it('MUST NOT register a duplicate username', async () => {
|
|
|
|
|
await util.createTestInvites(2);
|
2018-07-26 19:40:42 -04:00
|
|
|
|
const user0 = {displayname: 'user', password: 'pass', invite: 'code0'};
|
|
|
|
|
const user1 = {displayname: 'user', password: 'diff', invite: 'code1'};
|
2018-07-25 18:45:38 -04:00
|
|
|
|
|
2018-07-26 13:17:37 -04:00
|
|
|
|
await util.registerUser(user0, agent);
|
2018-08-01 16:53:55 -04:00
|
|
|
|
return verifyRejectedUsername(user1, 422, 'Username in use.');
|
2018-07-24 19:39:55 -04:00
|
|
|
|
});
|
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
it('MUST NOT register a username with a duplicate canonical name', async () => {
|
|
|
|
|
await util.createTestInvites(2);
|
2018-07-26 19:40:42 -04:00
|
|
|
|
const user0 = {displayname: 'bigbird', password: 'pass', invite: 'code0'};
|
|
|
|
|
const user1 = {displayname: 'ᴮᴵᴳᴮᴵᴿᴰ', password: 'diff', invite: 'code1'};
|
2018-07-25 18:45:38 -04:00
|
|
|
|
|
2018-07-26 13:17:37 -04:00
|
|
|
|
await util.registerUser(user0, agent);
|
2018-08-01 16:53:55 -04:00
|
|
|
|
return verifyRejectedUsername(user1, 422, 'Username in use.');
|
2018-07-24 19:39:55 -04:00
|
|
|
|
});
|
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
it('MUST NOT register a username containing whitespace', async () => {
|
|
|
|
|
await util.createTestInvites(3);
|
2018-07-24 19:39:55 -04:00
|
|
|
|
const users = [
|
2018-07-26 19:40:42 -04:00
|
|
|
|
{displayname: 'user name', password: 'pass', invite: 'code0'},
|
|
|
|
|
{displayname: 'user name', password: 'pass', invite: 'code1'},
|
|
|
|
|
{displayname: 'user name', password: 'pass', invite: 'code2'}
|
2018-07-24 19:39:55 -04:00
|
|
|
|
];
|
2018-07-25 18:45:38 -04:00
|
|
|
|
|
2018-08-01 12:34:15 -04:00
|
|
|
|
const failMsg = 'displayname contains invalid characters.';
|
2018-08-01 16:53:55 -04:00
|
|
|
|
return Promise.all(users.map(user => verifyRejectedUsername(user, 400, failMsg)));
|
2018-07-24 19:39:55 -04:00
|
|
|
|
});
|
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
it('MUST NOT register a username containing HTML', async () => {
|
|
|
|
|
await util.createTestInvite();
|
2018-07-26 19:40:42 -04:00
|
|
|
|
const user = {displayname: 'user<svg/onload=alert("XSS")>', password: 'pass', invite: 'code'};
|
2018-08-01 16:53:55 -04:00
|
|
|
|
return verifyRejectedUsername(user, 400, 'displayname contains invalid characters.');
|
2018-07-24 19:39:55 -04:00
|
|
|
|
});
|
2018-07-25 01:44:45 -04:00
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
it('MUST NOT register a username with too many characters', async () => {
|
|
|
|
|
await util.createTestInvite();
|
2018-07-26 19:40:42 -04:00
|
|
|
|
const user = {displayname: '123456789_123456789_123456789_1234567', password: 'pass', invite: 'code'};
|
2018-08-01 16:53:55 -04:00
|
|
|
|
return verifyRejectedUsername(user, 400, 'displayname too long.');
|
2018-07-29 11:06:57 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
2017-10-13 16:17:18 -04:00
|
|
|
|
});
|
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
describe('/POST login', () => {
|
|
|
|
|
async function verifySuccessfulLogin(credentials) {
|
2018-07-29 11:06:57 -04:00
|
|
|
|
// Login with the agent
|
2018-07-25 21:34:16 -04:00
|
|
|
|
const res = await util.login(credentials, agent);
|
2018-07-29 11:06:57 -04:00
|
|
|
|
util.verifyResponse(res, 200, 'Logged in.');
|
2018-07-25 21:34:16 -04:00
|
|
|
|
res.should.have.cookie('session.id');
|
|
|
|
|
|
2018-07-29 11:06:57 -04:00
|
|
|
|
// Get /api/auth/whoami, which can only be viewed when logged in
|
2018-07-26 13:17:37 -04:00
|
|
|
|
const whoami = await util.whoami(agent);
|
|
|
|
|
whoami.should.have.status(200);
|
2018-07-25 18:45:38 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function verifyFailedLogin(credentials) {
|
2018-07-26 13:17:37 -04:00
|
|
|
|
const res = await util.login(credentials, agent);
|
2018-07-29 11:06:57 -04:00
|
|
|
|
util.verifyResponse(res, 401, 'Unauthorized.');
|
2018-07-25 18:45:38 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('0 Valid Request', () => {
|
|
|
|
|
it('SHOULD accept a valid user with a valid password', async () => {
|
2018-07-26 13:17:37 -04:00
|
|
|
|
await util.createTestUser(agent);
|
2018-07-26 19:40:42 -04:00
|
|
|
|
return verifySuccessfulLogin({displayname: 'user', password: 'pass'});
|
2018-07-25 18:45:38 -04:00
|
|
|
|
});
|
2018-07-26 13:17:37 -04:00
|
|
|
|
|
2018-07-29 11:06:57 -04:00
|
|
|
|
it('SHOULD accept a username instead of a displayname', async () => {
|
|
|
|
|
await util.createTestUser(agent);
|
|
|
|
|
return verifySuccessfulLogin({username: 'user', password: 'pass'});
|
|
|
|
|
});
|
2018-07-26 19:01:16 -04:00
|
|
|
|
|
2018-07-29 11:06:57 -04:00
|
|
|
|
it('SHOULD accept any non-normalized variant of a username with a valid password', async () => {
|
|
|
|
|
await util.createTestInvite();
|
|
|
|
|
await util.registerUser({displayname: 'ᴮᴵᴳᴮᴵᴿᴰ', password: 'pass', invite: 'code'}, agent);
|
|
|
|
|
return verifySuccessfulLogin({displayname: 'BiGbIrD', password: 'pass'});
|
|
|
|
|
});
|
2017-10-13 16:17:18 -04:00
|
|
|
|
});
|
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
describe('1 Invalid Password', () => {
|
|
|
|
|
it('SHOULD NOT accept an invalid password', async () => {
|
2018-07-26 13:17:37 -04:00
|
|
|
|
await util.createTestUser(agent);
|
2018-07-26 19:40:42 -04:00
|
|
|
|
return verifyFailedLogin({displayname: 'user', password: 'bogus'});
|
2018-07-25 18:45:38 -04:00
|
|
|
|
});
|
2017-10-13 16:17:18 -04:00
|
|
|
|
});
|
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
describe('2 Invalid User', () => {
|
|
|
|
|
it('SHOULD NOT accept an invalid user', async () =>
|
2018-07-26 19:40:42 -04:00
|
|
|
|
verifyFailedLogin({displayname: 'bogus', password: 'bogus'})
|
2018-07-25 18:45:38 -04:00
|
|
|
|
);
|
2017-10-13 16:17:18 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
2018-08-12 05:30:50 -04:00
|
|
|
|
|
2018-08-13 05:59:04 -04:00
|
|
|
|
describe('/POST logout', () => {
|
|
|
|
|
async function verifyNoSession() {
|
|
|
|
|
const res = await util.whoami(agent);
|
|
|
|
|
util.verifyResponse(res, 401, 'Unauthorized.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('0 Valid Request', () => {
|
|
|
|
|
it('must logout a user with a session', async () => {
|
|
|
|
|
await util.createTestSession(agent);
|
|
|
|
|
const res = await util.logout(agent);
|
|
|
|
|
util.verifyResponse(res, 200, 'Logged out.');
|
|
|
|
|
return verifyNoSession();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('1 Invalid Session', () => {
|
|
|
|
|
it('must not logout a user without a session', async () => {
|
|
|
|
|
const res = await util.logout(agent);
|
|
|
|
|
util.verifyResponse(res, 400, 'Not logged in.');
|
|
|
|
|
return verifyNoSession();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-08-12 05:30:50 -04:00
|
|
|
|
describe('/POST whoami', () => {
|
|
|
|
|
function verifyWhoami(res, username, displayname, scope, key) {
|
|
|
|
|
res.should.have.status(200);
|
|
|
|
|
res.body.should.be.a('object');
|
|
|
|
|
res.body.should.have.property('username').equal(username);
|
|
|
|
|
res.body.should.have.property('displayname').equal(displayname);
|
|
|
|
|
res.body.should.have.property('scope').deep.equal(scope);
|
|
|
|
|
res.body.should.have.property('key').equal(key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('0 Valid Request', () => {
|
|
|
|
|
it('must respond with a valid session', async () => {
|
|
|
|
|
await util.createTestSession(agent);
|
|
|
|
|
const res = await util.whoami(agent);
|
|
|
|
|
verifyWhoami(res, 'user', 'user', ['file.upload'], null);
|
|
|
|
|
return util.logout(agent);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('must respond with a valid api key', async () => {
|
|
|
|
|
await util.createTestKey(['file.upload']);
|
|
|
|
|
const res = await util.whoami(agent, 'key');
|
|
|
|
|
verifyWhoami(res, 'Mocha', 'Mocha', ['file.upload'], 'key');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('1 Invalid Auth', () => {
|
|
|
|
|
it('must not respond with an invalid session', async () => {
|
|
|
|
|
const res = await util.whoami(agent);
|
|
|
|
|
util.verifyResponse(res, 401, 'Unauthorized.');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('must not respond with a banned user with a valid session', async () => {
|
|
|
|
|
await util.createTestSession(agent);
|
|
|
|
|
await util.setBanned('user', true);
|
|
|
|
|
const res = await util.whoami(agent);
|
|
|
|
|
util.verifyResponse(res, 403, 'Forbidden.');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('must not respond with a banned users api key', async () => {
|
|
|
|
|
await util.createTestUser(agent);
|
|
|
|
|
await Promise.all([
|
|
|
|
|
util.setBanned('user', true),
|
|
|
|
|
util.insertKey({key: 'key', identifier: 'test', scope: ['file.upload'], issuer: 'user'})
|
|
|
|
|
]);
|
|
|
|
|
const res = await util.whoami(agent, 'key');
|
|
|
|
|
util.verifyResponse(res, 403, 'Forbidden.');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
2017-10-13 16:17:18 -04:00
|
|
|
|
});
|
|
|
|
|
|
2018-07-27 14:23:23 -04:00
|
|
|
|
describe('Uploading', () => {
|
2018-07-29 20:09:09 -04:00
|
|
|
|
after(async () => util.clearDirectory(config.get('Upload.path')));
|
|
|
|
|
|
2018-07-25 21:34:16 -04:00
|
|
|
|
describe('/POST upload', () => {
|
2018-07-28 17:13:19 -04:00
|
|
|
|
async function verifySuccessfulUpload(file, key) {
|
2018-07-26 16:54:08 -04:00
|
|
|
|
// Get file stats beforehand
|
2018-07-29 11:06:57 -04:00
|
|
|
|
const fileHash = await util.fileHash(file);
|
2018-07-26 16:54:08 -04:00
|
|
|
|
|
|
|
|
|
// Submit the upload and verify the result
|
2018-07-28 17:13:19 -04:00
|
|
|
|
const res = await util.upload(file, agent, key);
|
2018-07-26 13:17:37 -04:00
|
|
|
|
res.should.have.status(200);
|
|
|
|
|
res.body.should.be.a('object');
|
|
|
|
|
res.body.should.have.property('url');
|
2018-07-29 11:06:57 -04:00
|
|
|
|
const idLength = config.get('Upload.idLength');
|
|
|
|
|
res.body.should.have.property('id').length(idLength, 'The ID should be a ' + idLength + ' letter lowercase string.');
|
2018-07-26 16:54:08 -04:00
|
|
|
|
|
|
|
|
|
// Find the uploaded file in the database
|
|
|
|
|
const upload = await Upload.findOne({id: res.body.id}, {_id: 0, id: 1, file: 1});
|
|
|
|
|
const uploadFile = upload.file.path;
|
|
|
|
|
upload.should.be.a('object');
|
2018-07-29 11:06:57 -04:00
|
|
|
|
upload.id.should.equal(res.body.id, 'The uploaded file in the database should exist and match the reponse ID.');
|
2018-07-26 16:54:08 -04:00
|
|
|
|
|
|
|
|
|
// Verify the uploaded file is the same as the file now on disk
|
2018-07-29 11:06:57 -04:00
|
|
|
|
const uploadHash = await util.fileHash(uploadFile);
|
|
|
|
|
uploadHash.should.equal(fileHash, 'The uploaded file and the file on disk should have matching hashes.');
|
2018-07-28 17:13:19 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function verifySuccessfulUserUpload(file, username) {
|
|
|
|
|
// Get the user's stats beforehand
|
|
|
|
|
const userBefore = await User.findOne({username: username}, {_id: 0, uploadCount: 1, uploadSize: 1});
|
|
|
|
|
|
2018-07-29 11:06:57 -04:00
|
|
|
|
await verifySuccessfulUpload(file);
|
2018-07-28 17:13:19 -04:00
|
|
|
|
|
2018-07-26 16:54:08 -04:00
|
|
|
|
// Verify the user's stats have been updated correctly
|
2018-07-26 19:40:42 -04:00
|
|
|
|
const userAfter = await User.findOne({username: username}, {_id: 0, uploadCount: 1, uploadSize: 1});
|
2018-07-29 11:06:57 -04:00
|
|
|
|
const fileSize = await util.fileSize(file);
|
|
|
|
|
userAfter.uploadCount.should.equal(userBefore.uploadCount + 1, 'The users upload count should be incremented.');
|
|
|
|
|
userAfter.uploadSize.should.equal(userBefore.uploadSize + fileSize, 'The users upload size should be properly increased.');
|
2018-07-26 13:17:37 -04:00
|
|
|
|
}
|
2018-07-25 21:34:16 -04:00
|
|
|
|
|
2018-07-28 17:13:19 -04:00
|
|
|
|
async function verifySuccessfulKeyUpload(file, key) {
|
|
|
|
|
// Get the key's stats beforehand
|
|
|
|
|
const keyBefore = await Key.findOne({key: key}, {_id: 0, uploadCount: 1, uploadSize: 1});
|
2018-07-26 19:40:42 -04:00
|
|
|
|
|
2018-07-29 11:06:57 -04:00
|
|
|
|
await verifySuccessfulUpload(file, key);
|
2018-07-28 17:13:19 -04:00
|
|
|
|
|
|
|
|
|
// Verify the key's stats have been updated correctly
|
|
|
|
|
const keyAfter = await Key.findOne({key: key}, {_id: 0, uploadCount: 1, uploadSize: 1});
|
2018-07-29 11:06:57 -04:00
|
|
|
|
const fileSize = await util.fileSize(file);
|
|
|
|
|
keyAfter.uploadCount.should.equal(keyBefore.uploadCount + 1, 'The keys upload count should be incremented.');
|
|
|
|
|
keyAfter.uploadSize.should.equal(keyBefore.uploadSize + fileSize, 'The keys upload size should be properly increased');
|
2018-07-26 19:40:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-28 17:13:19 -04:00
|
|
|
|
async function verifyFailedUpload(file, status, message, key) {
|
2018-07-26 17:34:47 -04:00
|
|
|
|
const fileCountBefore = await util.directoryFileCount(config.get('Upload.path'));
|
2018-07-26 16:54:08 -04:00
|
|
|
|
const uploadCountBefore = await Upload.countDocuments({});
|
|
|
|
|
|
2018-07-28 17:13:19 -04:00
|
|
|
|
const res = await util.upload(file, agent, key);
|
2018-07-29 11:06:57 -04:00
|
|
|
|
util.verifyResponse(res, status, message);
|
2018-07-26 16:54:08 -04:00
|
|
|
|
|
2018-07-26 17:34:47 -04:00
|
|
|
|
const fileCountAfter = await util.directoryFileCount(config.get('Upload.path'));
|
2018-07-29 11:06:57 -04:00
|
|
|
|
fileCountAfter.should.equal(fileCountBefore, 'File should not be written to disk');
|
2018-07-26 16:54:08 -04:00
|
|
|
|
|
|
|
|
|
const uploadCountAfter = await Upload.countDocuments({});
|
2018-07-29 11:06:57 -04:00
|
|
|
|
uploadCountAfter.should.equal(uploadCountBefore, 'No uploads should be written to the database');
|
2018-07-26 13:17:37 -04:00
|
|
|
|
}
|
2018-07-25 21:34:16 -04:00
|
|
|
|
|
|
|
|
|
describe('0 Valid Request', () => {
|
2018-07-28 17:13:19 -04:00
|
|
|
|
it('SHOULD accept an upload from a valid session', async () => {
|
2018-07-26 13:17:37 -04:00
|
|
|
|
await Promise.all([
|
|
|
|
|
util.createTestSession(agent),
|
|
|
|
|
util.createTestFile(2048, 'test.bin')
|
|
|
|
|
]);
|
|
|
|
|
|
2018-07-28 17:13:19 -04:00
|
|
|
|
await verifySuccessfulUserUpload('test.bin', 'user');
|
2018-07-26 13:17:37 -04:00
|
|
|
|
|
|
|
|
|
return Promise.all([
|
|
|
|
|
util.logout(agent),
|
2018-07-26 16:54:08 -04:00
|
|
|
|
util.deleteFile('test.bin')
|
2018-07-26 13:17:37 -04:00
|
|
|
|
]);
|
2018-07-25 21:34:16 -04:00
|
|
|
|
});
|
2018-07-28 17:13:19 -04:00
|
|
|
|
|
|
|
|
|
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');
|
|
|
|
|
})
|
2017-10-13 16:17:18 -04:00
|
|
|
|
});
|
|
|
|
|
|
2018-07-25 21:34:16 -04:00
|
|
|
|
describe('1 Invalid Authentication', () => {
|
2018-07-28 17:13:19 -04:00
|
|
|
|
it('SHOULD NOT accept an unauthenticated request', async () => {
|
|
|
|
|
await util.createTestFile(2048, 'test.bin');
|
|
|
|
|
|
2018-07-29 20:09:09 -04:00
|
|
|
|
await verifyFailedUpload('test.bin', 401, 'Unauthorized.');
|
2018-07-28 17:13:19 -04:00
|
|
|
|
|
|
|
|
|
return util.deleteFile('test.bin');
|
|
|
|
|
});
|
2018-07-26 13:17:37 -04:00
|
|
|
|
|
2018-07-28 17:13:19 -04:00
|
|
|
|
it('SHOULD NOT accept a session request without file.upload scope', async () => {
|
|
|
|
|
await util.insertInvite({code: 'code', scope: [], issuer: 'Mocha'});
|
2018-07-26 19:40:42 -04:00
|
|
|
|
await util.registerUser({displayname: 'user', password: 'pass', invite: 'code'}, agent);
|
|
|
|
|
await util.login({displayname: 'user', password: 'pass'}, agent);
|
2018-07-26 13:17:37 -04:00
|
|
|
|
|
|
|
|
|
await util.createTestFile(2048, 'test.bin');
|
|
|
|
|
|
|
|
|
|
await verifyFailedUpload('test.bin', 403, 'Forbidden.');
|
2017-10-13 16:17:18 -04:00
|
|
|
|
|
2018-07-26 13:17:37 -04:00
|
|
|
|
return Promise.all([
|
|
|
|
|
util.logout(agent),
|
2018-07-26 16:54:08 -04:00
|
|
|
|
util.deleteFile('test.bin')
|
2018-07-26 13:17:37 -04:00
|
|
|
|
]);
|
2018-07-25 21:34:16 -04:00
|
|
|
|
});
|
2018-07-28 17:13:19 -04:00
|
|
|
|
|
|
|
|
|
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');
|
|
|
|
|
})
|
2018-07-25 21:34:16 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('3 Invalid File', () => {
|
2018-07-29 20:09:09 -04:00
|
|
|
|
before(() => util.createTestFile(config.get('Upload.maxSize') + 1024, 'large.bin'));
|
|
|
|
|
after(() => util.deleteFile('large.bin'));
|
2018-07-26 13:17:37 -04:00
|
|
|
|
|
2018-07-29 20:09:09 -04:00
|
|
|
|
it('SHOULD NOT accept a too large file', async () => {
|
|
|
|
|
await util.createTestSession(agent);
|
2018-07-26 13:17:37 -04:00
|
|
|
|
await verifyFailedUpload('large.bin', 413, 'File too large.');
|
2018-07-29 20:09:09 -04:00
|
|
|
|
return util.logout(agent);
|
2018-07-26 13:17:37 -04:00
|
|
|
|
});
|
2017-10-13 18:08:13 -04:00
|
|
|
|
});
|
2017-10-13 16:17:18 -04:00
|
|
|
|
|
2018-07-29 11:06:57 -04:00
|
|
|
|
describe('4 Malformed Request', () => {
|
2018-07-26 13:17:37 -04:00
|
|
|
|
it('SHOULD NOT accept a request with no file attached', async () => {
|
|
|
|
|
await util.createTestSession(agent);
|
2018-07-29 20:09:09 -04:00
|
|
|
|
await verifyFailedUpload(null, 400, 'Bad request.');
|
2018-07-25 21:34:16 -04:00
|
|
|
|
|
2018-07-26 13:17:37 -04:00
|
|
|
|
return util.logout(agent);
|
2018-07-28 17:13:19 -04:00
|
|
|
|
});
|
2018-07-29 20:09:09 -04:00
|
|
|
|
|
2018-08-14 08:30:46 -04:00
|
|
|
|
it('must only accept one file from a request with multiple files attached', async () => {
|
2018-07-29 20:09:09 -04:00
|
|
|
|
await Promise.all([
|
|
|
|
|
util.createTestFile(2048, 'test1.bin'),
|
|
|
|
|
util.createTestFile(2048, 'test2.bin'),
|
|
|
|
|
util.createTestSession(agent)
|
|
|
|
|
]);
|
|
|
|
|
|
2018-08-14 08:30:46 -04:00
|
|
|
|
|
|
|
|
|
const fileCountBefore = await util.directoryFileCount(config.get('Upload.path'));
|
|
|
|
|
const uploadCountBefore = await Upload.countDocuments({});
|
|
|
|
|
|
2018-07-29 20:09:09 -04:00
|
|
|
|
const res = await agent.post('/api/upload')
|
|
|
|
|
.attach('file', 'test1.bin', 'test1.bin')
|
|
|
|
|
.attach('file1', 'test2.bin', 'test2.bin');
|
|
|
|
|
|
2018-08-14 08:30:46 -04:00
|
|
|
|
util.verifyResponse(res, 200, 'File uploaded.');
|
|
|
|
|
|
|
|
|
|
const fileCountAfter = await util.directoryFileCount(config.get('Upload.path'));
|
|
|
|
|
fileCountAfter.should.equal(fileCountBefore + 1, 'Only one file should be written to the disk');
|
|
|
|
|
|
|
|
|
|
const uploadCountAfter = await Upload.countDocuments({});
|
|
|
|
|
uploadCountAfter.should.equal(uploadCountBefore + 1, 'Only one upload should be written to the database');
|
2018-07-29 20:09:09 -04:00
|
|
|
|
|
|
|
|
|
return Promise.all([
|
|
|
|
|
util.deleteFile('test1.bin'),
|
|
|
|
|
util.deleteFile('test2.bin')
|
|
|
|
|
]);
|
|
|
|
|
})
|
2018-07-28 17:13:19 -04:00
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-08-01 20:25:27 -04:00
|
|
|
|
describe('Viewing', () => {
|
|
|
|
|
async function verifyView(file, id, disposition) {
|
|
|
|
|
const viewsBefore = (await Upload.findOne({id: id})).views;
|
|
|
|
|
|
|
|
|
|
const res = await util.view(id, agent)
|
|
|
|
|
.parse(util.binaryFileParser);
|
|
|
|
|
|
|
|
|
|
res.should.have.status(200);
|
|
|
|
|
res.should.have.header('content-disposition', disposition);
|
|
|
|
|
|
|
|
|
|
const [uploadHash, downloadHash] = await Promise.all([
|
|
|
|
|
util.fileHash(file),
|
|
|
|
|
util.bufferHash(res.body)
|
|
|
|
|
]);
|
|
|
|
|
downloadHash.should.equal(uploadHash, 'Uploaded file and downloaded hash should match');
|
|
|
|
|
|
|
|
|
|
const viewsAfter = (await Upload.findOne({id: id})).views;
|
|
|
|
|
viewsAfter.should.equal(viewsBefore + 1, 'The files views should be incremented.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
it('must return an uploaded binary file', async () => {
|
|
|
|
|
await Promise.all([
|
|
|
|
|
util.createTestSession(agent),
|
|
|
|
|
util.createTestFile(2048, 'test.bin')
|
|
|
|
|
]);
|
|
|
|
|
const upload = await util.upload('test.bin', agent);
|
2018-08-01 20:28:21 -04:00
|
|
|
|
await verifyView('test.bin', upload.body.id, 'attachment; filename="test.bin"');
|
|
|
|
|
return util.deleteFile('test.bin');
|
2018-08-01 20:25:27 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('must return an uploaded image file inline', async () => {
|
|
|
|
|
await Promise.all([
|
|
|
|
|
util.createTestSession(agent),
|
|
|
|
|
util.createTestFile(2048, 'test.jpg')
|
|
|
|
|
]);
|
|
|
|
|
const upload = await util.upload('test.jpg', agent);
|
2018-08-01 20:28:21 -04:00
|
|
|
|
await verifyView('test.jpg', upload.body.id, 'inline');
|
|
|
|
|
return util.deleteFile('test.jpg');
|
2018-08-01 20:25:27 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('must return an error when file not found', async () => {
|
|
|
|
|
const res = await util.view('abcdef', agent);
|
|
|
|
|
util.verifyResponse(res, 404, 'File not found.');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-07-28 17:13:19 -04:00
|
|
|
|
describe('Invites', () => {
|
|
|
|
|
describe('/POST create', () => {
|
2018-07-29 11:06:57 -04:00
|
|
|
|
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]+$/, 'The invite should be a hex string.');
|
|
|
|
|
|
|
|
|
|
const dbInvite = await Invite.findOne({code: res.body.code});
|
|
|
|
|
dbInvite.should.not.equal(null);
|
|
|
|
|
dbInvite.scope.should.deep.equal(invite.scope, 'The created invites scope should match the request.');
|
|
|
|
|
dbInvite.issuer.should.equal('user');
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-28 17:13:19 -04:00
|
|
|
|
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.');
|
|
|
|
|
});
|
|
|
|
|
});
|
2017-10-13 16:17:18 -04:00
|
|
|
|
});
|
2018-07-28 17:13:19 -04:00
|
|
|
|
|
|
|
|
|
describe('/POST delete', () => {
|
2018-07-29 11:06:57 -04:00
|
|
|
|
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 be removed from the database.');
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-28 17:13:19 -04:00
|
|
|
|
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);
|
2018-08-01 16:53:55 -04:00
|
|
|
|
util.verifyResponse(res, 422, 'Invite not found.');
|
2018-07-28 17:13:19 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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);
|
2018-08-01 16:53:55 -04:00
|
|
|
|
util.verifyResponse(res, 422, 'Invite not found.');
|
2018-07-28 17:13:19 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('/POST get', () => {
|
2018-07-29 11:06:57 -04:00
|
|
|
|
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, 'Only one invite should be in the array');
|
|
|
|
|
res.body[0].code.should.equal(code, 'The found invite should match the request code');
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-28 17:13:19 -04:00
|
|
|
|
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');
|
2018-07-29 11:06:57 -04:00
|
|
|
|
res.body.should.have.length(0, 'No invites should be found.');
|
2018-07-28 17:13:19 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
2018-07-26 13:17:37 -04:00
|
|
|
|
});
|
2017-10-13 16:17:18 -04:00
|
|
|
|
|
2018-08-01 16:53:55 -04:00
|
|
|
|
describe('Keys', () => {
|
|
|
|
|
describe('/POST create', () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
async function verifyCreatedKey(key) {
|
|
|
|
|
const res = await util.createKey(key, agent);
|
|
|
|
|
util.verifyResponse(res, 200, 'Key created.');
|
|
|
|
|
res.body.should.have.property('key').match(/^[A-Fa-f0-9]+$/, 'The key should be a hex string');
|
|
|
|
|
|
|
|
|
|
const dbKey = await Key.findOne({key: res.body.key});
|
|
|
|
|
dbKey.should.not.equal(null);
|
|
|
|
|
dbKey.scope.should.deep.equal(key.scope, 'The created keys scope should match the request.');
|
|
|
|
|
dbKey.issuer.should.equal('user');
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-01 16:53:55 -04:00
|
|
|
|
describe('0 Valid Request', () => {
|
|
|
|
|
it('SHOULD create a key with valid scope from a valid session', async () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
await util.createSession(agent, ['key.create', 'file.upload']);
|
|
|
|
|
return verifyCreatedKey({identifier: 'key', scope: ['file.upload']});
|
2018-08-01 16:53:55 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('1 Invalid Scope', () => {
|
|
|
|
|
it('SHOULD NOT create a key without key.create scope', async () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
await util.createSession(agent, ['file.upload']);
|
|
|
|
|
const res = await util.createKey({identifier: 'key', scope: ['file.upload']}, agent);
|
|
|
|
|
util.verifyResponse(res, 403, 'Forbidden.');
|
2018-08-01 16:53:55 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('SHOULD NOT create a key with scope exceeding the requesters', async () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
await util.createSession(agent, ['key.create']);
|
|
|
|
|
const res = await util.createKey({identifier: 'key', scope: ['file.upload']}, agent);
|
|
|
|
|
util.verifyResponse(res, 403, 'Requested scope exceeds own scope.');
|
2018-08-01 16:53:55 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
2018-08-13 06:13:47 -04:00
|
|
|
|
|
|
|
|
|
describe('2 Key Limit', () => {
|
|
|
|
|
it('must not create additional keys beyond the limit', async () => {
|
|
|
|
|
await util.createSession(agent, ['key.create', 'file.upload']);
|
|
|
|
|
const limit = config.get('Key.limit');
|
|
|
|
|
|
|
|
|
|
// Create keys upto the limit (key0, key1, key2, ...)
|
|
|
|
|
await Promise.all(
|
|
|
|
|
[...Array(limit)]
|
|
|
|
|
.map(idx => util.createKey({identifier: 'key' + idx, scope: ['file.upload']}, agent)));
|
|
|
|
|
|
|
|
|
|
const res = await util.createKey({identifier: 'toomany', scope: ['file.upload']}, agent);
|
|
|
|
|
util.verifyResponse(res, 403, 'Key limit reached.');
|
|
|
|
|
});
|
|
|
|
|
});
|
2018-08-01 16:53:55 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('/POST delete', () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
async function verifyDeletedKey(key) {
|
|
|
|
|
const res = await util.deleteKey(key, agent);
|
|
|
|
|
util.verifyResponse(res, 200, 'Key deleted.');
|
|
|
|
|
|
|
|
|
|
const keyCount = await Key.countDocuments({key: key});
|
|
|
|
|
keyCount.should.equal(0, 'The key should be removed from the database.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-08-01 16:53:55 -04:00
|
|
|
|
describe('0 Valid Request', () => {
|
|
|
|
|
it('SHOULD delete a key with valid scope from a valid session', async () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
await util.createSession(agent, ['key.create', 'key.delete', 'file.upload']);
|
|
|
|
|
const key = await util.createKey({identifier: 'key', scope: ['file.upload']}, agent);
|
|
|
|
|
return verifyDeletedKey(key.body.key);
|
2018-08-01 16:53:55 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('SHOULD delete another users key with key.delete.others scope', async () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
await util.createSession(agent, ['key.create', 'file.upload'], 'alice');
|
|
|
|
|
const key = await util.createKey({identifier: 'key', scope: ['file.upload']}, agent);
|
|
|
|
|
await util.logout(agent);
|
|
|
|
|
await util.createSession(agent, ['key.delete', 'key.delete.others'], 'eve');
|
|
|
|
|
return verifyDeletedKey(key.body.key);
|
2018-08-01 16:53:55 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('1 Invalid Scope', () => {
|
|
|
|
|
it('SHOULD NOT delete another users key without key.delete.others scope', async () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
await util.createSession(agent, ['key.create', 'file.upload'], 'bob');
|
|
|
|
|
const key = await util.createKey({identifier: 'key', scope: ['file.upload']}, agent);
|
|
|
|
|
await util.logout(agent);
|
|
|
|
|
await util.createSession(agent, ['key.delete'], 'eve');
|
|
|
|
|
const res = await util.deleteKey(key.body.key, agent);
|
|
|
|
|
util.verifyResponse(res, 422, 'Key not found.');
|
2018-08-01 16:53:55 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('2 Invalid Key', () => {
|
|
|
|
|
it('SHOULD return an error when the key was not found', async () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
await util.createSession(agent, ['key.delete']);
|
|
|
|
|
const res = await util.deleteKey('bogus', agent);
|
|
|
|
|
util.verifyResponse(res, 422, 'Key not found.');
|
2018-08-01 16:53:55 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('/POST get', () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
async function verifyKeySearch(keys, query = {}) {
|
|
|
|
|
const res = await util.getKeys(query, agent);
|
|
|
|
|
res.should.have.status(200);
|
|
|
|
|
res.body.should.be.a('Array');
|
|
|
|
|
|
|
|
|
|
keys.sort();
|
|
|
|
|
const resKeys = res.body.map(key => key.key).sort();
|
|
|
|
|
|
|
|
|
|
resKeys.should.deep.equal(keys, 'All keys should be present in the result.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function verifySingleSearch(key, query = {}) {
|
|
|
|
|
const res = await util.getKeys(query, agent);
|
|
|
|
|
res.should.have.status(200);
|
|
|
|
|
res.body.should.be.a('Array');
|
|
|
|
|
res.body.should.have.length(1, 'Only one key should be in the array');
|
|
|
|
|
res.body[0].key.should.equal(key, 'The found key should match the request code');
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-01 16:53:55 -04:00
|
|
|
|
describe('0 Valid Request', () => {
|
|
|
|
|
it('SHOULD get multiple keys from a valid session', async () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
await util.createSession(agent, ['key.create', 'key.get', 'file.upload']);
|
|
|
|
|
const keys = await Promise.all([
|
|
|
|
|
util.createKey({identifier: 'key1', scope: ['file.upload']}, agent),
|
|
|
|
|
util.createKey({identifier: 'key2', scope: ['file.upload']}, agent)
|
|
|
|
|
]);
|
|
|
|
|
return verifyKeySearch(keys.map(res => res.body.key));
|
2018-08-01 16:53:55 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('SHOULD get a key by identifier from a valid session', async () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
await util.createSession(agent, ['key.create', 'key.get', 'file.upload']);
|
|
|
|
|
const keys = await Promise.all([
|
|
|
|
|
util.createKey({identifier: 'key1', scope: ['file.upload']}, agent),
|
|
|
|
|
util.createKey({identifier: 'key2', scope: ['file.upload']}, agent)
|
|
|
|
|
]);
|
|
|
|
|
return verifySingleSearch(keys[1].body.key, {identifier: 'key2'});
|
2018-08-01 16:53:55 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('SHOULD get another users key with key.get.others scope', async () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
await util.createSession(agent, ['key.create', 'file.upload'], 'bob');
|
|
|
|
|
const key1 = await util.createKey({identifier: 'key', scope: ['file.upload']}, agent);
|
|
|
|
|
await util.logout(agent);
|
|
|
|
|
await util.createSession(agent, ['key.create', 'key.get', 'key.get.others', 'file.upload']);
|
|
|
|
|
const key2 = await util.createKey({identifier: 'key', scope: ['file.upload']}, agent);
|
|
|
|
|
return verifyKeySearch([key1.body.key, key2.body.key]);
|
2018-08-01 16:53:55 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('1 Invalid Scope', () => {
|
2018-08-01 18:58:30 -04:00
|
|
|
|
it('SHOULD NOT get another users key without key.get.others scope', async () => {
|
|
|
|
|
await util.createSession(agent, ['key.create', 'file.upload'], 'alice');
|
|
|
|
|
await util.createInvite({identifier: 'private_key', scope: ['file.upload']}, agent);
|
|
|
|
|
await util.logout(agent);
|
2018-08-01 16:53:55 -04:00
|
|
|
|
|
2018-08-01 18:58:30 -04:00
|
|
|
|
await util.createSession(agent, ['key.get'], 'eve');
|
|
|
|
|
const res = await util.getKeys({identifier: 'private_key'}, agent);
|
|
|
|
|
res.should.have.status(200);
|
|
|
|
|
res.body.should.be.a('Array');
|
|
|
|
|
res.body.should.have.length(0, 'No invites should be found.');
|
2018-08-01 16:53:55 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-08-13 05:52:54 -04:00
|
|
|
|
describe('Users', () => {
|
|
|
|
|
describe('/GET get', () => {
|
|
|
|
|
beforeEach(() => Promise.all([
|
|
|
|
|
Promise.all([
|
|
|
|
|
util.insertInvite({code: 'test1', scope: ['file.upload'], issuer: 'Mocha'}),
|
|
|
|
|
util.insertInvite({code: 'test2', scope: ['file.upload'], issuer: 'Mocha'})
|
|
|
|
|
]),
|
|
|
|
|
Promise.all([
|
|
|
|
|
util.registerUser({displayname: 'user1', password: 'pass', invite: 'test1'}, agent),
|
|
|
|
|
util.registerUser({displayname: 'user2', password: 'pass', invite: 'test2'}, agent)
|
|
|
|
|
])
|
|
|
|
|
]));
|
|
|
|
|
|
|
|
|
|
async function verifyGetUsers(res, users) {
|
|
|
|
|
res.should.have.status(200);
|
|
|
|
|
res.body.should.be.a('Array');
|
|
|
|
|
res.body.map(user => user.username).sort().should.deep.equal(users.sort());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('0 Valid Request', () => {
|
|
|
|
|
it('must get all users with an empty query', async () => {
|
|
|
|
|
await util.createSession(agent, ['user.get'], 'admin');
|
|
|
|
|
const res = await util.getUsers({}, agent);
|
|
|
|
|
return verifyGetUsers(res, ['user1', 'user2', 'admin']);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('must filter users by username', async () => {
|
|
|
|
|
await util.createSession(agent, ['user.get'], 'admin');
|
|
|
|
|
const res = await util.getUsers({username: 'user1'}, agent);
|
|
|
|
|
return verifyGetUsers(res, ['user1']);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('must filter users by displayname', async () => {
|
|
|
|
|
await util.createSession(agent, ['user.get'], 'admin');
|
|
|
|
|
const res = await util.getUsers({displayname: 'user1'}, agent);
|
|
|
|
|
return verifyGetUsers(res, ['user1']);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('must return an empty array when no users were found', async () => {
|
|
|
|
|
await util.createSession(agent, ['user.get'], 'admin');
|
|
|
|
|
const res = await util.getUsers({username: 'abc'}, agent);
|
|
|
|
|
return verifyGetUsers(res, []);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('/POST ban/unban', () => {
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
await util.insertInvite({code: 'test1', scope: ['file.upload'], issuer: 'Mocha'});
|
|
|
|
|
await util.registerUser({displayname: 'user', password: 'pass', invite: 'test1'}, agent);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
async function verifyBanned(res, statusCode, message, username, banStatus) {
|
|
|
|
|
util.verifyResponse(res, statusCode, message);
|
|
|
|
|
const user = await User.findOne({username: username});
|
|
|
|
|
user.should.exist;
|
|
|
|
|
user.should.have.property('banned').equal(banStatus, 'The user should have banned status ' + banStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('0 Valid Request', () => {
|
|
|
|
|
it('must ban a not banned user', async () => {
|
|
|
|
|
await util.createSession(agent, ['user.ban'], 'admin');
|
|
|
|
|
const res = await util.ban('user', agent);
|
|
|
|
|
return verifyBanned(res, 200, 'User banned.', 'user', true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('must unban a banned user', async () => {
|
|
|
|
|
await util.setBanned('user', true);
|
|
|
|
|
await util.createSession(agent, ['user.unban'], 'admin');
|
|
|
|
|
const res = await util.unban('user', agent);
|
|
|
|
|
return verifyBanned(res, 200, 'User unbanned.', 'user', false);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('1 Already Requested Ban Status', () => {
|
|
|
|
|
it('must not ban an already banned user', async () => {
|
|
|
|
|
await util.setBanned('user', true);
|
|
|
|
|
await util.createSession(agent, ['user.ban'], 'admin');
|
|
|
|
|
const res = await util.ban('user', agent);
|
|
|
|
|
return verifyBanned(res, 422, 'User already banned.', 'user', true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('must not unban a not banned user', async () => {
|
|
|
|
|
await util.createSession(agent, ['user.unban'], 'admin');
|
|
|
|
|
const res = await util.unban('user', agent);
|
|
|
|
|
return verifyBanned(res, 422, 'User not banned.', 'user', false);
|
|
|
|
|
});
|
|
|
|
|
});
|
2018-08-13 06:23:46 -04:00
|
|
|
|
|
|
|
|
|
describe('2 Not Found', () => {
|
|
|
|
|
it('must not ban a nonexistant user', async () => {
|
|
|
|
|
await util.createSession(agent, ['user.ban'], 'admin');
|
|
|
|
|
const res = await util.ban('abc', agent);
|
|
|
|
|
util.verifyResponse(res, 422, 'User not found.');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('must not unban a nonexistant user', async () => {
|
|
|
|
|
await util.createSession(agent, ['user.unban'], 'admin');
|
|
|
|
|
const res = await util.unban('abc', agent);
|
|
|
|
|
util.verifyResponse(res, 422, 'User not found.');
|
|
|
|
|
});
|
|
|
|
|
});
|
2018-08-13 05:52:54 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-07-25 18:45:38 -04:00
|
|
|
|
after(() => server.close(() => process.exit(0)));
|