mirror of
https://github.com/Foltik/Shimapan
synced 2025-04-11 12:06:30 -04:00
Make use of username/displayname field consistent throughout api
This commit is contained in:
parent
82e9989b2b
commit
7441eaaf02
@ -6,12 +6,36 @@ var InviteSchema = mongoose.Schema({
|
|||||||
unique: true,
|
unique: true,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
scope: [String],
|
|
||||||
issuer: String,
|
scope: {
|
||||||
recipient: String,
|
type: [String],
|
||||||
issued: Date,
|
required: true
|
||||||
used: Date,
|
},
|
||||||
exp: Date
|
|
||||||
|
issuer: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
|
||||||
|
recipient: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
|
||||||
|
issued: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
},
|
||||||
|
|
||||||
|
used: {
|
||||||
|
type: Date,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
|
||||||
|
exp: {
|
||||||
|
type: Date,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/*InviteSchema.methods.use = function(canonicalname, cb) {
|
/*InviteSchema.methods.use = function(canonicalname, cb) {
|
||||||
|
@ -1,22 +1,41 @@
|
|||||||
var mongoose = require('mongoose');
|
const mongoose = require('mongoose');
|
||||||
|
|
||||||
|
const KeySchema = mongoose.Schema({
|
||||||
|
key: {
|
||||||
|
type: String,
|
||||||
|
unique: true,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
|
||||||
var KeySchema = mongoose.Schema({
|
|
||||||
key: String,
|
|
||||||
identifier: {
|
identifier: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
scope: [String],
|
|
||||||
|
scope: {
|
||||||
|
type: [String],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
|
||||||
uploadCount: {
|
uploadCount: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
},
|
},
|
||||||
|
|
||||||
uploadSize: {
|
uploadSize: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
},
|
},
|
||||||
username: String,
|
|
||||||
date: Date
|
issuer: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
|
||||||
|
date: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = mongoose.model('Key', KeySchema);
|
module.exports = mongoose.model('Key', KeySchema);
|
@ -7,26 +7,36 @@ var UserSchema = mongoose.Schema({
|
|||||||
unique: true,
|
unique: true,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
canonicalname: {
|
|
||||||
|
displayname: {
|
||||||
type: String,
|
type: String,
|
||||||
unique: true,
|
unique: true,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
scope: [String],
|
|
||||||
|
scope: {
|
||||||
|
type: [String],
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
|
||||||
uploadCount: {
|
uploadCount: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
},
|
},
|
||||||
|
|
||||||
uploadSize: {
|
uploadSize: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
},
|
},
|
||||||
date: Date
|
|
||||||
|
date: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
UserSchema.plugin(passportLocalMongoose, {
|
UserSchema.plugin(passportLocalMongoose, {
|
||||||
usernameField: 'canonicalname',
|
saltlen: 64,
|
||||||
saltlen: 32,
|
|
||||||
iterations: 10000,
|
iterations: 10000,
|
||||||
limitAttempts: true
|
limitAttempts: true
|
||||||
});
|
});
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const User = require('../models/User.js');
|
|
||||||
const Invite = require('../models/Invite.js');
|
|
||||||
const passport = require('passport');
|
|
||||||
const config = require('config');
|
const config = require('config');
|
||||||
|
|
||||||
|
const ModelPath = '../models/';
|
||||||
|
const User = require(ModelPath + 'User.js');
|
||||||
|
const Invite = require(ModelPath + 'Invite.js');
|
||||||
|
|
||||||
|
const passport = require('passport');
|
||||||
|
|
||||||
const canonicalizeRequest = require('../util/canonicalize').canonicalizeRequest;
|
const canonicalizeRequest = require('../util/canonicalize').canonicalizeRequest;
|
||||||
const requireAuth = require('../util/requireAuth').requireAuth;
|
const requireAuth = require('../util/requireAuth').requireAuth;
|
||||||
const wrap = require('../util/wrap.js').wrap;
|
const wrap = require('../util/wrap.js').wrap;
|
||||||
|
|
||||||
|
|
||||||
// Wraps passport.authenticate to return a promise
|
// Wraps passport.authenticate to return a promise
|
||||||
function authenticate(req, res, next) {
|
function authenticate(req, res, next) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
@ -28,16 +28,16 @@ function login(user, req) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a canonical name is valid
|
// Check if the requested username is valid
|
||||||
async function validateUsername(username, canonicalName, sanitize) {
|
async function validateUsername(username, sanitize) {
|
||||||
if (canonicalName.length > config.get('User.Username.maxLength'))
|
if (username.length > config.get('User.Username.maxLength'))
|
||||||
return {valid: false, message: 'Username too long.'};
|
return {valid: false, message: 'Username too long.'};
|
||||||
|
|
||||||
const restrictedRegex = new RegExp(config.get('User.Username.restrictedChars'), 'g');
|
const restrictedRegex = new RegExp(config.get('User.Username.restrictedChars'), 'g');
|
||||||
if (canonicalName !== sanitize(canonicalName).replace(restrictedRegex, ''))
|
if (username !== sanitize(username).replace(restrictedRegex, ''))
|
||||||
return {valid: false, message: 'Username contains invalid characters.'};
|
return {valid: false, message: 'Username contains invalid characters.'};
|
||||||
|
|
||||||
const count = await User.countDocuments({canonicalname: canonicalName});
|
const count = await User.countDocuments({username: username});
|
||||||
|
|
||||||
if (count !== 0)
|
if (count !== 0)
|
||||||
return {valid: false, message: 'Username in use.'};
|
return {valid: false, message: 'Username in use.'};
|
||||||
@ -55,19 +55,19 @@ async function validateInvite(code) {
|
|||||||
if (invite.used)
|
if (invite.used)
|
||||||
return {valid: false, message: 'Invite already used.'};
|
return {valid: false, message: 'Invite already used.'};
|
||||||
|
|
||||||
if (invite.exp < Date.now())
|
if (invite.exp != null && invite.exp < Date.now())
|
||||||
return {valid: false, message: 'Invite expired.'};
|
return {valid: false, message: 'Invite expired.'};
|
||||||
|
|
||||||
return {valid: true, invite: invite};
|
return {valid: true, invite: invite};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
router.post('/register', canonicalizeRequest, wrap(async (req, res, next) => {
|
router.post('/register', canonicalizeRequest, wrap(async (req, res) => {
|
||||||
// Validate the invite and username
|
// Validate the invite and username
|
||||||
const [inviteStatus, usernameStatus] =
|
const [inviteStatus, usernameStatus] =
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
validateInvite(req.body.invite),
|
validateInvite(req.body.invite),
|
||||||
validateUsername(req.body.username, req.body.canonicalname, req.sanitize)
|
validateUsername(req.body.username, req.sanitize)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Error if validation failed
|
// Error if validation failed
|
||||||
@ -80,11 +80,11 @@ router.post('/register', canonicalizeRequest, wrap(async (req, res, next) => {
|
|||||||
await Promise.all([
|
await Promise.all([
|
||||||
User.register({
|
User.register({
|
||||||
username: req.body.username,
|
username: req.body.username,
|
||||||
canonicalname: req.body.canonicalname,
|
displayname: req.body.displayname,
|
||||||
scope: inviteStatus.invite.scope,
|
scope: inviteStatus.invite.scope,
|
||||||
date: Date.now()
|
date: Date.now()
|
||||||
}, req.body.password),
|
}, req.body.password),
|
||||||
Invite.updateOne({code: inviteStatus.invite.code}, {recipient: req.body.canonicalname, used: Date.now()})
|
Invite.updateOne({code: inviteStatus.invite.code}, {recipient: req.body.username, used: Date.now()})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
res.status(200).json({'message': 'Registration successful.'});
|
res.status(200).json({'message': 'Registration successful.'});
|
||||||
@ -100,7 +100,7 @@ router.post('/login', canonicalizeRequest, wrap(async (req, res, next) => {
|
|||||||
await login(user, req);
|
await login(user, req);
|
||||||
|
|
||||||
// Set session vars
|
// Set session vars
|
||||||
req.session.passport.display = user.username;
|
req.session.passport.displayname = user.displayname;
|
||||||
req.session.passport.scope = user.scope;
|
req.session.passport.scope = user.scope;
|
||||||
|
|
||||||
res.status(200).json({'message': 'Logged in.'});
|
res.status(200).json({'message': 'Logged in.'});
|
||||||
@ -113,10 +113,10 @@ router.post('/logout', function (req, res) {
|
|||||||
|
|
||||||
router.get('/whoami', requireAuth(), (req, res) => {
|
router.get('/whoami', requireAuth(), (req, res) => {
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
user: req.authUser,
|
user: req.username,
|
||||||
display: req.authDisplay,
|
display: req.displayname,
|
||||||
scope: req.authScope,
|
scope: req.scope,
|
||||||
key: req.authKey
|
key: req.key
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -32,9 +32,9 @@ const generateId = async () => {
|
|||||||
|
|
||||||
const updateStats = async req =>
|
const updateStats = async req =>
|
||||||
Promise.all([
|
Promise.all([
|
||||||
User.updateOne({username: req.authUser}, {$inc: {uploadCount: 1, uploadSize: req.file.size}}),
|
User.updateOne({username: req.username}, {$inc: {uploadCount: 1, uploadSize: req.file.size}}),
|
||||||
req.authKey
|
req.key
|
||||||
? Key.updateOne({key: req.authKey}, {$inc: {uploadCount: 1, uploadSize: req.file.size}})
|
? Key.updateOne({key: req.key}, {$inc: {uploadCount: 1, uploadSize: req.file.size}})
|
||||||
: Promise.resolve()
|
: Promise.resolve()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -50,8 +50,8 @@ router.post('/', requireAuth('file.upload'), fileUpload, wrap(async (req, res) =
|
|||||||
|
|
||||||
const upload = {
|
const upload = {
|
||||||
id: await generateId(),
|
id: await generateId(),
|
||||||
uploader: req.authUser,
|
uploader: req.username,
|
||||||
uploaderKey: req.authKey,
|
uploaderKey: req.key,
|
||||||
date: Date.now(),
|
date: Date.now(),
|
||||||
file: req.file
|
file: req.file
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// Normalizes, decomposes, and lowercases a utf-8 string
|
// Normalizes, decomposes, and lowercases a utf-8 string
|
||||||
exports.canonicalize = (username) => username.normalize('NFKD').toLowerCase();
|
exports.canonicalize = displayname => displayname.normalize('NFKD').toLowerCase();
|
||||||
|
|
||||||
exports.canonicalizeRequest =
|
exports.canonicalizeRequest =
|
||||||
(req, res, next) => {
|
(req, res, next) => {
|
||||||
req.body.canonicalname = exports.canonicalize(req.body.username);
|
req.body.username = exports.canonicalize(req.body.displayname);
|
||||||
next();
|
next();
|
||||||
};
|
};
|
@ -12,10 +12,10 @@ exports.requireAuth = scope =>
|
|||||||
wrap(async (req, res, next) => {
|
wrap(async (req, res, next) => {
|
||||||
if (req.isAuthenticated()) {
|
if (req.isAuthenticated()) {
|
||||||
if (scope ? verifyScope(req.session.passport.scope, scope) : true) {
|
if (scope ? verifyScope(req.session.passport.scope, scope) : true) {
|
||||||
req.authUser = req.session.passport.user;
|
req.username = req.session.passport.user;
|
||||||
req.authDisplay = req.session.passport.display;
|
req.displayname = req.session.passport.displayname;
|
||||||
req.authScope = req.session.passport.scope;
|
req.scope = req.session.passport.scope;
|
||||||
req.authKey = null;
|
req.key = null;
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
res.status(403).json({message: 'Forbidden.'});
|
res.status(403).json({message: 'Forbidden.'});
|
||||||
@ -23,10 +23,10 @@ exports.requireAuth = scope =>
|
|||||||
} else if (req.body.apikey) {
|
} else if (req.body.apikey) {
|
||||||
const key = await Key.findOne({key: apikey});
|
const key = await Key.findOne({key: apikey});
|
||||||
if (scope ? verifyScope(key.scope, scope) : true) {
|
if (scope ? verifyScope(key.scope, scope) : true) {
|
||||||
req.authUser = key.username;
|
req.username = key.username;
|
||||||
req.authDisplay = key.username;
|
req.displayname = key.username;
|
||||||
req.authScope = key.scope;
|
req.scope = key.scope;
|
||||||
req.authKey = key.key;
|
req.key = key.key;
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
res.status(403).json({message: 'Forbidden.'});
|
res.status(403).json({message: 'Forbidden.'});
|
||||||
|
61
test/api.js
61
test/api.js
@ -2,6 +2,7 @@ process.env.NODE_ENV = 'test';
|
|||||||
|
|
||||||
const chai = require('chai');
|
const chai = require('chai');
|
||||||
chai.use(require('chai-http'));
|
chai.use(require('chai-http'));
|
||||||
|
const should = chai.should();
|
||||||
|
|
||||||
const ModelPath = '../app/models/';
|
const ModelPath = '../app/models/';
|
||||||
const User = require(ModelPath + 'User.js');
|
const User = require(ModelPath + 'User.js');
|
||||||
@ -43,26 +44,26 @@ describe('Accounts', function() {
|
|||||||
res.body.should.be.a('object');
|
res.body.should.be.a('object');
|
||||||
res.body.should.have.property('message').eql('Registration successful.');
|
res.body.should.have.property('message').eql('Registration successful.');
|
||||||
|
|
||||||
const userCount = await User.countDocuments({username: user.username});
|
const userCount = await User.countDocuments({displayname: user.displayname});
|
||||||
userCount.should.equal(1);
|
userCount.should.equal(1);
|
||||||
|
|
||||||
const inviteCount = await Invite.countDocuments({code: user.invite, recipient: canonicalize(user.username)});
|
const inviteCount = await Invite.countDocuments({code: user.invite, recipient: canonicalize(user.displayname)});
|
||||||
inviteCount.should.equal(1);
|
inviteCount.should.equal(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('MUST register a valid user with a valid invite', async () =>
|
it('MUST register a valid user with a valid invite', async () =>
|
||||||
verifySuccessfulRegister({username: 'user', password: 'pass', invite: 'code'})
|
verifySuccessfulRegister({displayname: 'user', password: 'pass', invite: 'code'})
|
||||||
);
|
);
|
||||||
|
|
||||||
it('MUST register a username with unicode symbols and a valid invite', async () =>
|
it('MUST register a username with unicode symbols and a valid invite', async () =>
|
||||||
verifySuccessfulRegister({username: 'ᴮᴵᴳᴮᴵᴿᴰ', password: 'pass', invite: 'code'})
|
verifySuccessfulRegister({displayname: 'ᴮᴵᴳᴮᴵᴿᴰ', password: 'pass', invite: 'code'})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('1 Invalid Invites', () => {
|
describe('1 Invalid Invites', () => {
|
||||||
async function verifyRejectedInvite(invite, message) {
|
async function verifyRejectedInvite(invite, message) {
|
||||||
const user = {username: 'user', password: 'pass', invite: 'code'};
|
const user = {displayname: 'user', password: 'pass', invite: 'code'};
|
||||||
if (invite) {
|
if (invite) {
|
||||||
await util.createInvite(invite, agent);
|
await util.createInvite(invite, agent);
|
||||||
user.invite = invite.code;
|
user.invite = invite.code;
|
||||||
@ -73,7 +74,7 @@ describe('Accounts', function() {
|
|||||||
res.body.should.be.a('object');
|
res.body.should.be.a('object');
|
||||||
res.body.should.have.property('message').eql(message);
|
res.body.should.have.property('message').eql(message);
|
||||||
|
|
||||||
const inviteCount = await Invite.countDocuments({code: user.invite, recipient: canonicalize(user.username)});
|
const inviteCount = await Invite.countDocuments({code: user.invite, recipient: canonicalize(user.displayname)});
|
||||||
inviteCount.should.equal(0);
|
inviteCount.should.equal(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,30 +83,30 @@ describe('Accounts', function() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
it('MUST NOT register a used invite', async () =>
|
it('MUST NOT register a used invite', async () =>
|
||||||
verifyRejectedInvite({code: 'code', used: new Date()}, 'Invite already used.')
|
verifyRejectedInvite({code: 'code', used: new Date(), issuer: 'Mocha'}, 'Invite already used.')
|
||||||
);
|
);
|
||||||
|
|
||||||
it('MUST NOT register an expired invite', async () =>
|
it('MUST NOT register an expired invite', async () =>
|
||||||
verifyRejectedInvite({code: 'code', exp: new Date()}, 'Invite expired.')
|
verifyRejectedInvite({code: 'code', exp: new Date(), issuer: 'Mocha'}, 'Invite expired.')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('2 Invalid Usernames', () => {
|
describe('2 Invalid Displaynames', () => {
|
||||||
async function verifyRejectedUsername(user, message) {
|
async function verifyRejectedUsername(user, message) {
|
||||||
const res = await util.registerUser(user, agent);
|
const res = await util.registerUser(user, agent);
|
||||||
res.should.have.status(422);
|
res.should.have.status(422);
|
||||||
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);
|
||||||
|
|
||||||
const inviteCount = await Invite.countDocuments({code: user.invite, recipient: canonicalize(user.username)});
|
const inviteCount = await Invite.countDocuments({code: user.invite, recipient: canonicalize(user.displayname)});
|
||||||
inviteCount.should.equal(0);
|
inviteCount.should.equal(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('MUST NOT register a duplicate username', async () => {
|
it('MUST NOT register a duplicate username', async () => {
|
||||||
await util.createTestInvites(2);
|
await util.createTestInvites(2);
|
||||||
const user0 = {username: 'user', password: 'pass', invite: 'code0'};
|
const user0 = {displayname: 'user', password: 'pass', invite: 'code0'};
|
||||||
const user1 = {username: 'user', password: 'diff', invite: 'code1'};
|
const user1 = {displayname: 'user', password: 'diff', invite: 'code1'};
|
||||||
|
|
||||||
await util.registerUser(user0, agent);
|
await util.registerUser(user0, agent);
|
||||||
return verifyRejectedUsername(user1, 'Username in use.');
|
return verifyRejectedUsername(user1, 'Username in use.');
|
||||||
@ -113,8 +114,8 @@ describe('Accounts', function() {
|
|||||||
|
|
||||||
it('MUST NOT register a username with a duplicate canonical name', async () => {
|
it('MUST NOT register a username with a duplicate canonical name', async () => {
|
||||||
await util.createTestInvites(2);
|
await util.createTestInvites(2);
|
||||||
const user0 = {username: 'bigbird', password: 'pass', invite: 'code0'};
|
const user0 = {displayname: 'bigbird', password: 'pass', invite: 'code0'};
|
||||||
const user1 = {username: 'ᴮᴵᴳᴮᴵᴿᴰ', password: 'diff', invite: 'code1'};
|
const user1 = {displayname: 'ᴮᴵᴳᴮᴵᴿᴰ', password: 'diff', invite: 'code1'};
|
||||||
|
|
||||||
await util.registerUser(user0, agent);
|
await util.registerUser(user0, agent);
|
||||||
return verifyRejectedUsername(user1, 'Username in use.');
|
return verifyRejectedUsername(user1, 'Username in use.');
|
||||||
@ -123,9 +124,9 @@ describe('Accounts', function() {
|
|||||||
it('MUST NOT register a username containing whitespace', async () => {
|
it('MUST NOT register a username containing whitespace', async () => {
|
||||||
await util.createTestInvites(3);
|
await util.createTestInvites(3);
|
||||||
const users = [
|
const users = [
|
||||||
{username: 'user name', password: 'pass', invite: 'code0'},
|
{displayname: 'user name', password: 'pass', invite: 'code0'},
|
||||||
{username: 'user name', password: 'pass', invite: 'code1'},
|
{displayname: 'user name', password: 'pass', invite: 'code1'},
|
||||||
{username: 'user name', password: 'pass', invite: 'code2'}
|
{displayname: 'user name', password: 'pass', invite: 'code2'}
|
||||||
];
|
];
|
||||||
|
|
||||||
const failMsg = 'Username contains invalid characters.';
|
const failMsg = 'Username contains invalid characters.';
|
||||||
@ -134,13 +135,13 @@ describe('Accounts', function() {
|
|||||||
|
|
||||||
it('MUST NOT register a username containing HTML', async () => {
|
it('MUST NOT register a username containing HTML', async () => {
|
||||||
await util.createTestInvite();
|
await util.createTestInvite();
|
||||||
const user = {username: 'user<svg/onload=alert("XSS")>', password: 'pass', invite: 'code'};
|
const user = {displayname: 'user<svg/onload=alert("XSS")>', password: 'pass', invite: 'code'};
|
||||||
return verifyRejectedUsername(user, 'Username contains invalid characters.');
|
return verifyRejectedUsername(user, 'Username contains invalid characters.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('MUST NOT register a username with too many characters', async () => {
|
it('MUST NOT register a username with too many characters', async () => {
|
||||||
await util.createTestInvite();
|
await util.createTestInvite();
|
||||||
const user = {username: '123456789_123456789_123456789_1234567', password: 'pass', invite: 'code'};
|
const user = {displayname: '123456789_123456789_123456789_1234567', password: 'pass', invite: 'code'};
|
||||||
return verifyRejectedUsername(user, 'Username too long.');
|
return verifyRejectedUsername(user, 'Username too long.');
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -167,7 +168,7 @@ describe('Accounts', function() {
|
|||||||
describe('0 Valid Request', () => {
|
describe('0 Valid Request', () => {
|
||||||
it('SHOULD accept a valid user with a valid password', async () => {
|
it('SHOULD accept a valid user with a valid password', async () => {
|
||||||
await util.createTestUser(agent);
|
await util.createTestUser(agent);
|
||||||
return verifySuccessfulLogin({username: 'user', password: 'pass'});
|
return verifySuccessfulLogin({displayname: 'user', password: 'pass'});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('SHOULD accept any non-normalized variant of a username with a valid password', async () => {
|
it('SHOULD accept any non-normalized variant of a username with a valid password', async () => {
|
||||||
@ -179,13 +180,13 @@ describe('Accounts', function() {
|
|||||||
describe('1 Invalid Password', () => {
|
describe('1 Invalid Password', () => {
|
||||||
it('SHOULD NOT accept an invalid password', async () => {
|
it('SHOULD NOT accept an invalid password', async () => {
|
||||||
await util.createTestUser(agent);
|
await util.createTestUser(agent);
|
||||||
return verifyFailedLogin({username: 'user', password: 'bogus'});
|
return verifyFailedLogin({displayname: 'user', password: 'bogus'});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('2 Invalid User', () => {
|
describe('2 Invalid User', () => {
|
||||||
it('SHOULD NOT accept an invalid user', async () =>
|
it('SHOULD NOT accept an invalid user', async () =>
|
||||||
verifyFailedLogin({username: 'bogus', password: 'bogus'})
|
verifyFailedLogin({displayname: 'bogus', password: 'bogus'})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -195,12 +196,12 @@ describe('Uploads', () => {
|
|||||||
beforeEach(async () => util.clearDatabase());
|
beforeEach(async () => util.clearDatabase());
|
||||||
|
|
||||||
describe('/POST upload', () => {
|
describe('/POST upload', () => {
|
||||||
async function verifySuccessfulUpload(file, user) {
|
async function verifySuccessfulUpload(file, username) {
|
||||||
// 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
|
// Get the user stats beforehand
|
||||||
const userBefore = await User.findOne({canonicalname: user}, {_id: 0, uploadCount: 1, uploadSize: 1});
|
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);
|
||||||
@ -221,11 +222,15 @@ describe('Uploads', () => {
|
|||||||
uploadSize.should.equal(fileSize);
|
uploadSize.should.equal(fileSize);
|
||||||
|
|
||||||
// Verify the user's stats have been updated correctly
|
// Verify the user's stats have been updated correctly
|
||||||
const userAfter = await User.findOne({canonicalname: user}, {_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 verifyFailedUpload(file, status, message) {
|
async function verifyFailedUpload(file, status, message) {
|
||||||
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({});
|
||||||
@ -264,9 +269,9 @@ describe('Uploads', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
it('SHOULD NOT accept a request without file.upload scope', async () => {
|
it('SHOULD NOT accept a request without file.upload scope', async () => {
|
||||||
await util.createInvite({code: 'code', scope: []});
|
await util.createInvite({code: 'code', scope: [], issuer: 'Mocha'});
|
||||||
await util.registerUser({username: 'user', password: 'pass', invite: 'code'}, agent);
|
await util.registerUser({displayname: 'user', password: 'pass', invite: 'code'}, agent);
|
||||||
await util.login({username: 'user', password: 'pass'}, agent);
|
await util.login({displayname: 'user', password: 'pass'}, agent);
|
||||||
|
|
||||||
await util.createTestFile(2048, 'test.bin');
|
await util.createTestFile(2048, 'test.bin');
|
||||||
|
|
||||||
|
@ -2,12 +2,12 @@ process.env.NODE_ENV = 'test';
|
|||||||
|
|
||||||
const chai = require('chai');
|
const chai = require('chai');
|
||||||
chai.use(require('chai-http'));
|
chai.use(require('chai-http'));
|
||||||
const should = chai.should();
|
|
||||||
|
|
||||||
const User = require('../app/models/User.js');
|
const ModelPath = '../app/models/';
|
||||||
const Invite = require('../app/models/Invite.js');
|
const User = require(ModelPath + 'User.js');
|
||||||
const Upload = require('../app/models/Upload.js');
|
const Upload = require(ModelPath + 'Upload.js');
|
||||||
const Key = require('../app/models/Key.js');
|
const Key = require(ModelPath + 'Key.js');
|
||||||
|
const Invite = require(ModelPath + 'Invite.js');
|
||||||
|
|
||||||
const Buffer = require('buffer').Buffer;
|
const Buffer = require('buffer').Buffer;
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
@ -51,22 +51,22 @@ exports.whoami = (agent) =>
|
|||||||
//---------------- TEST ENTRY CREATION ----------------//
|
//---------------- TEST ENTRY CREATION ----------------//
|
||||||
|
|
||||||
exports.createTestInvite = () =>
|
exports.createTestInvite = () =>
|
||||||
exports.createInvite({code: 'code', scope: ['file.upload']});
|
exports.createInvite({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}))
|
.map(code => exports.createInvite({code: code, scope: ['file.upload'], issuer: 'Mocha'}))
|
||||||
);
|
);
|
||||||
|
|
||||||
exports.createTestUser = async agent => {
|
exports.createTestUser = async agent => {
|
||||||
await exports.createTestInvite();
|
await exports.createTestInvite();
|
||||||
return exports.registerUser({username: 'user', password: 'pass', invite: 'code'}, agent);
|
return exports.registerUser({displayname: 'user', password: 'pass', invite: 'code'}, agent);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.createTestSession = async agent => {
|
exports.createTestSession = async agent => {
|
||||||
await exports.createTestUser(agent);
|
await exports.createTestUser(agent);
|
||||||
return exports.login({username: 'user', password: 'pass'}, agent);
|
return exports.login({displayname: 'user', password: 'pass'}, agent);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.createTestFile = (size, name) =>
|
exports.createTestFile = (size, name) =>
|
||||||
|
Loading…
Reference in New Issue
Block a user