Merge commit 'b43c80e2c097b11114f4e4f01b9718321721a89b'

* commit 'b43c80e2c097b11114f4e4f01b9718321721a89b':
  feat(build): Update dependencies (#1847)
  fix(travis): Fix Travis failing on webdriver issues (#1845)
  fix(eslint): Inconsistent spacing before function parentheses (#1844)
  fix(mongodb): update ssl connection settings (#1809)
  Remove deprecated crypto package (#1843)
  feat(config): Mongo Seed 2.0 (#1808)
  fix(users): don't fail on missing old image on image upload (#1839)
  feat(build): Turn on mangling for uglify (#1841)
  fix(gulp): fix broken test:server:watch task (#1842)
  feat(core): Enhancement page title directive (#1686)
  feat(user): Add email support to forgot password (#1834)
  fix(mocha): update mochajs version to reduce vulnerabilities (#1830)
  refactor(menus): Refactor to the Menus client service to use functional loops/filters (#1575)
  feat(config): Mongoose 4.11 upgrade (#1818)

# Conflicts:
#	config/env/development.js
#	config/lib/app.js
#	modules/articles/server/models/article.server.model.js
#	modules/chat/client/config/chat.client.routes.js
#	modules/core/client/directives/page-title.client.directive.js
#	modules/core/client/services/menu.client.service.js
#	modules/users/client/config/users-admin.client.routes.js
#	modules/users/client/views/password/forgot-password.client.view.html
#	modules/users/server/models/user.server.model.js
#	package.json
This commit is contained in:
OldHawk
2017-08-22 13:35:29 +08:00
41 changed files with 1589 additions and 933 deletions

View File

@@ -41,7 +41,11 @@ module.exports = {
'one-var': [0, 'never'],
'one-var-declaration-per-line': [2, 'always'],
'padded-blocks': 0,
'space-before-function-paren': 0,
'space-before-function-paren': ['error', {
'anonymous': 'always',
'named': 'never',
'asyncArrow': 'always'
}],
'space-in-parens': [2, 'never'],
'spaced-comment': 0,
strict: 0,

View File

@@ -31,6 +31,7 @@ addons:
- clang
- google-chrome-stable
before_install:
- npm install webdriver-manager -g && webdriver-manager update
- npm install nsp -g
# - npm install snyk -g
- 'export PATH=$PATH:/usr/lib/chromium-browser/'
@@ -44,7 +45,8 @@ after_script:
- nsp check
# - snyk test
- gulp test:coverage
- node_modules/.bin/lcov-result-merger 'coverage/**/lcov.info' | node_modules/coveralls/bin/coveralls.js
- npm install lcov-result-merger@~1.2.0 -g
- lcov-result-merger 'coverage/**/lcov.info' | node_modules/coveralls/bin/coveralls.js
notifications:
webhooks:
urls:

View File

@@ -73,25 +73,53 @@ module.exports = {
seedDB: {
seed: process.env.MONGO_SEED === 'true',
options: {
logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false',
seedUser: {
username: process.env.MONGO_SEED_USER_USERNAME || 'seeduser',
provider: 'local',
email: process.env.MONGO_SEED_USER_EMAIL || 'user@localhost.com',
firstName: 'User',
lastName: 'Local',
displayName: 'User Local',
roles: ['user']
logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false'
},
// Order of collections in configuration will determine order of seeding.
// i.e. given these settings, the User seeds will be complete before
// Article seed is performed.
collections: [{
model: 'User',
docs: [{
data: {
username: 'local-admin',
email: 'admin@localhost.com',
firstName: 'Admin',
lastName: 'Local',
roles: ['admin', 'user']
}
}, {
// Set to true to overwrite this document
// when it already exists in the collection.
// If set to false, or missing, the seed operation
// will skip this document to avoid overwriting it.
overwrite: true,
data: {
username: 'local-user',
email: 'user@localhost.com',
firstName: 'User',
lastName: 'Local',
roles: ['user']
}
}]
}, {
model: 'Article',
options: {
// Override log results setting at the
// collection level.
logResults: true
},
seedAdmin: {
username: process.env.MONGO_SEED_ADMIN_USERNAME || 'seedadmin',
provider: 'local',
email: process.env.MONGO_SEED_ADMIN_EMAIL || 'admin@localhost.com',
firstName: 'Admin',
lastName: 'Local',
displayName: 'Admin Local',
roles: ['user', 'admin']
}
}
skip: {
// Skip collection when this query returns results.
// e.g. {}: Only seeds collection when it is empty.
when: {} // Mongoose qualified query
},
docs: [{
data: {
title: 'First Article',
content: 'This is a seeded Article for the development environment'
}
}]
}]
}
};

View File

@@ -20,10 +20,7 @@
module.exports = {
db: {
uri: 'mongodb://localhost/local-dev',
options: {
user: '',
pass: ''
}
options: {}
},
sessionSecret: process.env.SESSION_SECRET || 'youshouldchangethistosomethingsecret',
facebook: {

View File

@@ -15,22 +15,20 @@ module.exports = {
db: {
uri: process.env.MONGOHQ_URL || process.env.MONGODB_URI || 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/mean-dev-v2',
options: {
user: '',
pass: ''
/**
* Uncomment to enable ssl certificate based authentication to mongodb
* servers. Adjust the settings below for your specific certificate
* setup.
* for connect to a replicaset, rename server:{...} to replset:{...}
server: {
ssl: true,
sslValidate: false,
checkServerIdentity: false,
sslCA: fs.readFileSync('./config/sslcerts/ssl-ca.pem'),
sslCert: fs.readFileSync('./config/sslcerts/ssl-cert.pem'),
sslKey: fs.readFileSync('./config/sslcerts/ssl-key.pem'),
sslPass: '1234'
}
* Uncomment to enable ssl certificate based authentication to mongodb
* servers. Adjust the settings below for your specific certificate
* setup.
* for connect to a replicaset, rename server:{...} to replset:{...}
ssl: true,
sslValidate: false,
checkServerIdentity: false,
sslCA: fs.readFileSync('./config/sslcerts/ssl-ca.pem'),
sslCert: fs.readFileSync('./config/sslcerts/ssl-cert.pem'),
sslKey: fs.readFileSync('./config/sslcerts/ssl-key.pem'),
sslPass: '1234'
*/
},
// Enable mongoose debug mode
@@ -93,25 +91,19 @@ module.exports = {
seedDB: {
seed: process.env.MONGO_SEED === 'true',
options: {
logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false',
seedUser: {
username: process.env.MONGO_SEED_USER_USERNAME || 'seeduser',
provider: 'local',
email: process.env.MONGO_SEED_USER_EMAIL || 'user@localhost.com',
firstName: 'User',
lastName: 'Local',
displayName: 'User Local',
roles: ['user']
},
seedAdmin: {
username: process.env.MONGO_SEED_ADMIN_USERNAME || 'seedadmin',
provider: 'local',
email: process.env.MONGO_SEED_ADMIN_EMAIL || 'admin@localhost.com',
firstName: 'Admin',
lastName: 'Local',
displayName: 'Admin Local',
roles: ['user', 'admin']
}
}
logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false'
},
collections: [{
model: 'User',
docs: [{
data: {
username: 'local-admin',
email: 'admin@localhost.com',
firstName: 'Admin',
lastName: 'Local',
roles: ['admin', 'user']
}
}]
}]
}
};

59
config/env/test.js vendored
View File

@@ -5,10 +5,7 @@ var defaultEnvConfig = require('./default');
module.exports = {
db: {
uri: process.env.MONGOHQ_URL || process.env.MONGODB_URI || 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/mean-test',
options: {
user: '',
pass: ''
},
options: {},
// Enable mongoose debug mode
debug: process.env.MONGODB_DEBUG || false
},
@@ -83,25 +80,39 @@ module.exports = {
seedDB: {
seed: process.env.MONGO_SEED === 'true',
options: {
logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false',
seedUser: {
username: process.env.MONGO_SEED_USER_USERNAME || 'seeduser',
provider: 'local',
email: process.env.MONGO_SEED_USER_EMAIL || 'user@localhost.com',
firstName: 'User',
lastName: 'Local',
displayName: 'User Local',
roles: ['user']
},
seedAdmin: {
username: process.env.MONGO_SEED_ADMIN_USERNAME || 'seedadmin',
provider: 'local',
email: process.env.MONGO_SEED_ADMIN_EMAIL || 'admin@localhost.com',
firstName: 'Admin',
lastName: 'Local',
displayName: 'Admin Local',
roles: ['user', 'admin']
}
}
// Default to not log results for tests
logResults: process.env.MONGO_SEED_LOG_RESULTS === 'true'
},
collections: [{
model: 'User',
docs: [{
overwrite: true,
data: {
username: 'seedadmin',
email: 'admin@localhost.com',
firstName: 'Admin',
lastName: 'Local',
roles: ['admin', 'user']
}
}, {
overwrite: true,
data: {
username: 'seeduser',
email: 'user@localhost.com',
firstName: 'User',
lastName: 'Local',
roles: ['user']
}
}]
}, {
model: 'Article',
docs: [{
overwrite: true,
data: {
title: 'Test Article',
content: 'Code coverage test article!'
}
}]
}]
}
};

View File

@@ -4,10 +4,10 @@
* Module dependencies.
*/
var config = require('../config'),
mongoose = require('./mongoose'),
mongooseService = require('./mongoose'),
express = require('./express'),
chalk = require('chalk'),
seed = require('./seed'),
seed = require('./mongo-seed'),
ircConfig = config.meanTorrentConfig.ircAnnounce;
function seedDB() {
@@ -17,11 +17,11 @@ function seedDB() {
}
}
// Initialize Models
mongoose.loadModels(seedDB);
module.exports.init = function init(callback) {
mongoose.connect(function (db) {
mongooseService.connect(function (db) {
// Initialize Models
mongooseService.loadModels(seedDB);
// Initialize express
var app = express.init(db);
if (callback) callback(app, db, config);

View File

@@ -119,7 +119,7 @@ module.exports.initSession = function (app, db) {
},
name: config.sessionKey,
store: new MongoStore({
mongooseConnection: db.connection,
db: db,
collection: config.sessionCollection
})
}));
@@ -131,9 +131,9 @@ module.exports.initSession = function (app, db) {
/**
* Invoke modules server configuration
*/
module.exports.initModulesConfiguration = function (app, db) {
module.exports.initModulesConfiguration = function (app) {
config.files.server.configs.forEach(function (configPath) {
require(path.resolve(configPath))(app, db);
require(path.resolve(configPath))(app);
});
};

View File

@@ -29,7 +29,7 @@ var logger = new winston.Logger({
// Useful for integrating with stream-related mechanism like Morgan's stream
// option to log all HTTP requests to a file
logger.stream = {
write: function(msg) {
write: function (msg) {
logger.info(msg);
}
};

153
config/lib/mongo-seed.js Normal file
View File

@@ -0,0 +1,153 @@
'use strict';
var _ = require('lodash'),
config = require('../config'),
mongoose = require('mongoose'),
chalk = require('chalk');
exports.start = start;
function start(seedConfig) {
return new Promise(function (resolve, reject) {
seedConfig = seedConfig || {};
var options = seedConfig.options || (config.seedDB ? _.clone(config.seedDB.options, true) : {});
var collections = seedConfig.collections || (config.seedDB ? _.clone(config.seedDB.collections, true) : []);
if (!collections.length) {
return resolve();
}
var seeds = collections
.filter(function (collection) {
return collection.model;
});
// Use the reduction pattern to ensure we process seeding in desired order.
seeds.reduce(function (p, item) {
return p.then(function () {
return seed(item, options);
});
}, Promise.resolve()) // start with resolved promise for initial previous (p) item
.then(onSuccessComplete)
.catch(onError);
// Local Promise handlers
function onSuccessComplete() {
if (options.logResults) {
console.log();
console.log(chalk.bold.green('Database Seeding: Mongo Seed complete!'));
console.log();
}
return resolve();
}
function onError(err) {
if (options.logResults) {
console.log();
console.log(chalk.bold.red('Database Seeding: Mongo Seed Failed!'));
console.log(chalk.bold.red('Database Seeding: ' + err));
console.log();
}
return reject(err);
}
});
}
function seed(collection, options) {
// Merge options with collection options
options = _.merge(options || {}, collection.options || {});
return new Promise(function (resolve, reject) {
const Model = mongoose.model(collection.model);
const docs = collection.docs;
var skipWhen = collection.skip ? collection.skip.when : null;
if (!Model.seed) {
return reject(new Error('Database Seeding: Invalid Model Configuration - ' + collection.model + '.seed() not implemented'));
}
if (!docs || !docs.length) {
return resolve();
}
// First check if we should skip this collection
// based on the collection's "skip.when" option.
// NOTE: If it exists, "skip.when" should be a qualified
// Mongoose query that will be used with Model.find().
skipCollection()
.then(seedDocuments)
.then(function () {
return resolve();
})
.catch(function (err) {
return reject(err);
});
function skipCollection() {
return new Promise(function (resolve, reject) {
if (!skipWhen) {
return resolve(false);
}
Model
.find(skipWhen)
.exec(function (err, results) {
if (err) {
return reject(err);
}
if (results && results.length) {
return resolve(true);
}
return resolve(false);
});
});
}
function seedDocuments(skipCollection) {
return new Promise(function (resolve, reject) {
if (skipCollection) {
return onComplete([{ message: chalk.yellow('Database Seeding: ' + collection.model + ' collection skipped') }]);
}
var workload = docs
.filter(function (doc) {
return doc.data;
})
.map(function (doc) {
return Model.seed(doc.data, { overwrite: doc.overwrite });
});
Promise.all(workload)
.then(onComplete)
.catch(onError);
// Local Closures
function onComplete(responses) {
if (options.logResults) {
responses.forEach(function (response) {
if (response.message) {
console.log(chalk.magenta(response.message));
}
});
}
return resolve();
}
function onError(err) {
return reject(err);
}
});
}
});
}

View File

@@ -3,7 +3,8 @@
/**
* Module dependencies.
*/
var config = require('../config'),
var _ = require('lodash'),
config = require('../config'),
chalk = require('chalk'),
path = require('path'),
mongoose = require('mongoose');
@@ -19,30 +20,31 @@ module.exports.loadModels = function (callback) {
};
// Initialize Mongoose
module.exports.connect = function (cb) {
var _this = this;
module.exports.connect = function (callback) {
mongoose.Promise = config.db.promise;
var db = mongoose.connect(config.db.uri, config.db.options, function (err) {
// Log Error
if (err) {
console.error(chalk.red('Could not connect to MongoDB!'));
console.log(err);
} else {
var options = _.merge(config.db.options || {}, { useMongoClient: true });
mongoose
.connect(config.db.uri, options)
.then(function (connection) {
// Enabling mongoose debug mode if required
mongoose.set('debug', config.db.debug);
// Call callback FN
if (cb) cb(db);
}
});
if (callback) callback(connection.db);
})
.catch(function (err) {
console.error(chalk.red('Could not connect to MongoDB!'));
console.log(err);
});
};
module.exports.disconnect = function (cb) {
mongoose.disconnect(function (err) {
console.info(chalk.yellow('Disconnected from MongoDB.'));
cb(err);
});
mongoose.connection.db
.close(function (err) {
console.info(chalk.yellow('Disconnected from MongoDB.'));
return cb(err);
});
};

View File

@@ -1,158 +0,0 @@
'use strict';
var _ = require('lodash'),
config = require('../config'),
mongoose = require('mongoose'),
chalk = require('chalk'),
crypto = require('crypto');
// global seed options object
var seedOptions = {};
function removeUser (user) {
return new Promise(function (resolve, reject) {
var User = mongoose.model('User');
User.find({ username: user.username }).remove(function (err) {
if (err) {
reject(new Error('Failed to remove local ' + user.username));
}
resolve();
});
});
}
function saveUser (user) {
return function() {
return new Promise(function (resolve, reject) {
// Then save the user
user.save(function (err, theuser) {
if (err) {
reject(new Error('Failed to add local ' + user.username));
} else {
resolve(theuser);
}
});
});
};
}
function checkUserNotExists (user) {
return new Promise(function (resolve, reject) {
var User = mongoose.model('User');
User.find({ username: user.username }, function (err, users) {
if (err) {
reject(new Error('Failed to find local account ' + user.username));
}
if (users.length === 0) {
resolve();
} else {
reject(new Error('Failed due to local account already exists: ' + user.username));
}
});
});
}
function reportSuccess (password) {
return function (user) {
return new Promise(function (resolve, reject) {
if (seedOptions.logResults) {
console.log(chalk.bold.red('Database Seeding:\t\t\tLocal ' + user.username + ' added with password set to ' + password));
}
resolve();
});
};
}
// save the specified user with the password provided from the resolved promise
function seedTheUser (user) {
return function (password) {
return new Promise(function (resolve, reject) {
var User = mongoose.model('User');
// set the new password
user.password = password;
if (user.username === seedOptions.seedAdmin.username && process.env.NODE_ENV === 'production') {
checkUserNotExists(user)
.then(saveUser(user))
.then(reportSuccess(password))
.then(function () {
resolve();
})
.catch(function (err) {
reject(err);
});
} else {
removeUser(user)
.then(saveUser(user))
.then(reportSuccess(password))
.then(function () {
resolve();
})
.catch(function (err) {
reject(err);
});
}
});
};
}
// report the error
function reportError (reject) {
return function (err) {
if (seedOptions.logResults) {
console.log();
console.log('Database Seeding:\t\t\t' + err);
console.log();
}
reject(err);
};
}
module.exports.start = function start(options) {
// Initialize the default seed options
seedOptions = _.clone(config.seedDB.options, true);
// Check for provided options
if (_.has(options, 'logResults')) {
seedOptions.logResults = options.logResults;
}
if (_.has(options, 'seedUser')) {
seedOptions.seedUser = options.seedUser;
}
if (_.has(options, 'seedAdmin')) {
seedOptions.seedAdmin = options.seedAdmin;
}
var User = mongoose.model('User');
return new Promise(function (resolve, reject) {
var adminAccount = new User(seedOptions.seedAdmin);
var userAccount = new User(seedOptions.seedUser);
// If production only seed admin if it does not exist
if (process.env.NODE_ENV === 'production') {
User.generateRandomPassphrase()
.then(seedTheUser(adminAccount))
.then(function () {
resolve();
})
.catch(reportError(reject));
} else {
// Add both Admin and User account
User.generateRandomPassphrase()
.then(seedTheUser(userAccount))
.then(User.generateRandomPassphrase)
.then(seedTheUser(adminAccount))
.then(function () {
resolve();
})
.catch(reportError(reject));
}
});
};

View File

@@ -74,7 +74,7 @@ module.exports = function (app, db) {
// Create a MongoDB storage object
var mongoStore = new MongoStore({
mongooseConnection: db.connection,
db: db,
collection: config.sessionCollection
});

View File

@@ -109,11 +109,10 @@ gulp.task('watch:server:run-tests', function () {
if (filePath === path.resolve(file.path)) {
changedTestFiles.push(f);
plugins.refresh.changed(f);
}
});
});
plugins.refresh.changed();
});
});
@@ -153,7 +152,7 @@ gulp.task('uglify', function () {
return gulp.src(assets)
.pipe(plugins.ngAnnotate())
.pipe(plugins.uglify({
mangle: false
mangle: true
}))
.pipe(plugins.concat('application.min.js'))
.pipe(plugins.rev())
@@ -304,15 +303,15 @@ gulp.task('templatecache', function () {
// Mocha tests task
gulp.task('mocha', function (done) {
// Open mongoose connections
var mongoose = require('./config/lib/mongoose.js');
var mongooseService = require('./config/lib/mongoose');
var testSuites = changedTestFiles.length ? changedTestFiles : testAssets.tests.server;
var error;
// Connect mongoose
mongoose.connect(function () {
mongoose.loadModels();
// Run the tests
mongooseService.connect(function (db) {
// Load mongoose models
mongooseService.loadModels();
gulp.src(testSuites)
.pipe(plugins.mocha({
reporter: 'spec',
@@ -323,9 +322,13 @@ gulp.task('mocha', function (done) {
error = err;
})
.on('end', function () {
// When the tests are done, disconnect mongoose and pass the error state back to gulp
mongoose.disconnect(function () {
done(error);
mongooseService.disconnect(function (err) {
if (err) {
console.log('Error disconnecting from database');
console.log(err);
}
return done(error);
});
});
});
@@ -360,7 +363,7 @@ gulp.task('karma', function (done) {
});
// Run karma with coverage options set and write report
gulp.task('karma:coverage', function(done) {
gulp.task('karma:coverage', function (done) {
new KarmaServer({
configFile: __dirname + '/karma.conf.js',
preprocessors: {
@@ -389,20 +392,54 @@ gulp.task('karma:coverage', function(done) {
// Drops the MongoDB database, used in e2e testing
gulp.task('dropdb', function (done) {
// Use mongoose configuration
var mongoose = require('./config/lib/mongoose.js');
var mongooseService = require('./config/lib/mongoose');
mongoose.connect(function (db) {
db.connection.db.dropDatabase(function (err) {
mongooseService.connect(function (db) {
db.dropDatabase(function (err) {
if (err) {
console.error(err);
} else {
console.log('Successfully dropped db: ', db.connection.db.databaseName);
console.log('Successfully dropped db: ', db.databaseName);
}
db.connection.db.close(done);
mongooseService.disconnect(done);
});
});
});
// Seed Mongo database based on configuration
gulp.task('mongo-seed', function (done) {
var db = require('./config/lib/mongoose');
var seed = require('./config/lib/mongo-seed');
// Open mongoose database connection
db.connect(function () {
db.loadModels();
seed
.start({
options: {
logResults: true
}
})
.then(function () {
// Disconnect and finish task
db.disconnect(done);
})
.catch(function (err) {
db.disconnect(function (disconnectError) {
if (disconnectError) {
console.log('Error disconnecting from the database, but was preceded by a Mongo Seed error.');
}
// Finish task with error
done(err);
});
});
});
});
// Downloads the selenium webdriver if protractor version is compatible
gulp.task('webdriver_update', webdriver_update);
@@ -417,12 +454,12 @@ gulp.task('protractor', ['webdriver_update'], function () {
.pipe(protractor({
configFile: 'protractor.conf.js'
}))
.on('end', function() {
.on('end', function () {
console.log('E2E Testing complete');
// exit with success.
process.exit(0);
})
.on('error', function(err) {
.on('error', function (err) {
console.error('E2E Tests failed:');
console.error(err);
process.exit(1);
@@ -475,3 +512,17 @@ gulp.task('default', function (done) {
gulp.task('prod', function (done) {
runSequence(['copyLocalEnvConfig', 'makeUploadsDir', 'templatecache'], 'build', 'env:prod', 'lint', ['nodemon-nodebug', 'watch'], done);
});
// Run Mongo Seed with default environment config
gulp.task('seed', function (done) {
runSequence('env:dev', 'mongo-seed', done);
});
// Run Mongo Seed with production environment config
gulp.task('seed:prod', function (done) {
runSequence('env:prod', 'mongo-seed', done);
});
gulp.task('seed:test', function (done) {
runSequence('env:test', 'mongo-seed', done);
});

View File

@@ -41,7 +41,8 @@
controller: 'ArticlesAdminController',
controllerAs: 'vm',
data: {
roles: ['admin']
roles: ['admin'],
pageTitle: '{{ articleResolve.title }}'
},
resolve: {
articleResolve: getArticle

View File

@@ -18,10 +18,7 @@
url: '',
templateUrl: '/modules/articles/client/views/list-articles.client.view.html',
controller: 'ArticlesListController',
controllerAs: 'vm',
data: {
pageTitle: 'Articles List'
}
controllerAs: 'vm'
})
.state('articles.view', {
url: '/:articleId',
@@ -32,7 +29,7 @@
articleResolve: getArticle
},
data: {
pageTitle: 'Article {{ articleResolve.title }}'
pageTitle: '{{ articleResolve.title }}'
}
});
}

View File

@@ -1,4 +1,4 @@
(function () {
(function () {
'use strict';
angular
@@ -19,7 +19,7 @@
// Remove existing Article
function remove() {
if ($window.confirm('Are you sure you want to delete?')) {
vm.article.$remove(function() {
vm.article.$remove(function () {
$state.go('admin.articles.list');
Notification.success({ message: '<i class="glyphicon glyphicon-ok"></i> Article deleted successfully!' });
});

View File

@@ -9,6 +9,6 @@ var path = require('path'),
/**
* Module init function.
*/
module.exports = function (app, db) {
module.exports = function (app) {
};

View File

@@ -4,7 +4,10 @@
* Module dependencies
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
Schema = mongoose.Schema,
path = require('path'),
config = require(path.resolve('./config/config')),
chalk = require('chalk');
/**
* Article Schema
@@ -35,6 +38,105 @@ var ArticleSchema = new Schema({
}
});
ArticleSchema.index({user: -1, created: -1});
ArticleSchema.statics.seed = seed;
mongoose.model('Article', ArticleSchema);
/**
* Seeds the User collection with document (Article)
* and provided options.
*/
function seed(doc, options) {
var Article = mongoose.model('Article');
return new Promise(function (resolve, reject) {
skipDocument()
.then(findAdminUser)
.then(add)
.then(function (response) {
return resolve(response);
})
.catch(function (err) {
return reject(err);
});
function findAdminUser(skip) {
var User = mongoose.model('User');
return new Promise(function (resolve, reject) {
if (skip) {
return resolve(true);
}
User
.findOne({
roles: { $in: ['admin'] }
})
.exec(function (err, admin) {
if (err) {
return reject(err);
}
doc.user = admin;
return resolve();
});
});
}
function skipDocument() {
return new Promise(function (resolve, reject) {
Article
.findOne({
title: doc.title
})
.exec(function (err, existing) {
if (err) {
return reject(err);
}
if (!existing) {
return resolve(false);
}
if (existing && !options.overwrite) {
return resolve(true);
}
// Remove Article (overwrite)
existing.remove(function (err) {
if (err) {
return reject(err);
}
return resolve(false);
});
});
});
}
function add(skip) {
return new Promise(function (resolve, reject) {
if (skip) {
return resolve({
message: chalk.yellow('Database Seeding: Article\t' + doc.title + ' skipped')
});
}
var article = new Article(doc);
article.save(function (err) {
if (err) {
return reject(err);
}
return resolve({
message: 'Database Seeding: Article\t' + article.title + ' added'
});
});
});
}
});
}

View File

@@ -23,7 +23,7 @@ var app,
describe('Article Admin CRUD tests', function () {
before(function (done) {
// Get application
app = express.init(mongoose);
app = express.init(mongoose.connection.db);
agent = request.agent(app);
done();

View File

@@ -24,7 +24,7 @@ describe('Article CRUD tests', function () {
before(function (done) {
// Get application
app = express.init(mongoose);
app = express.init(mongoose.connection.db);
agent = request.agent(app);
done();
@@ -119,7 +119,7 @@ describe('Article CRUD tests', function () {
// Save the article
articleObj.save(function () {
// Request articles
request(app).get('/api/articles')
agent.get('/api/articles')
.end(function (req, res) {
// Set assertion
res.body.should.be.instanceof(Array).and.have.lengthOf(1);
@@ -137,7 +137,7 @@ describe('Article CRUD tests', function () {
// Save the article
articleObj.save(function () {
request(app).get('/api/articles/' + articleObj._id)
agent.get('/api/articles/' + articleObj._id)
.end(function (req, res) {
// Set assertion
res.body.should.be.instanceof(Object).and.have.property('title', article.title);
@@ -150,7 +150,7 @@ describe('Article CRUD tests', function () {
it('should return proper error for single article with an invalid Id, if not signed in', function (done) {
// test is not a valid mongoose Id
request(app).get('/api/articles/test')
agent.get('/api/articles/test')
.end(function (req, res) {
// Set assertion
res.body.should.be.instanceof(Object).and.have.property('message', 'Article is invalid');
@@ -162,7 +162,7 @@ describe('Article CRUD tests', function () {
it('should return proper error for single article which doesnt exist, if not signed in', function (done) {
// This is a valid mongoose Id but a non-existent article
request(app).get('/api/articles/559e9cd815f80b4c256a8f41')
agent.get('/api/articles/559e9cd815f80b4c256a8f41')
.end(function (req, res) {
// Set assertion
res.body.should.be.instanceof(Object).and.have.property('message', 'No article with that identifier has been found');
@@ -202,7 +202,7 @@ describe('Article CRUD tests', function () {
// Save the article
articleObj.save(function () {
// Try deleting article
request(app).delete('/api/articles/' + articleObj._id)
agent.delete('/api/articles/' + articleObj._id)
.expect(403)
.end(function (articleDeleteErr, articleDeleteRes) {
// Set message assertion
@@ -312,7 +312,7 @@ describe('Article CRUD tests', function () {
if (err) {
return done(err);
}
request(app).get('/api/articles/' + articleObj._id)
agent.get('/api/articles/' + articleObj._id)
.end(function (req, res) {
// Set assertion
res.body.should.be.instanceof(Object).and.have.property('title', article.title);

View File

@@ -15,8 +15,7 @@
controller: 'ChatController',
controllerAs: 'vm',
data: {
roles: ['user', 'oper', 'admin'],
pageTitle: 'Chat'
roles: ['user', 'oper', 'admin']
}
});
}

View File

@@ -39,13 +39,12 @@
controller: 'ErrorController',
controllerAs: 'vm',
params: {
message: function($stateParams) {
message: function ($stateParams) {
return $stateParams.message;
}
},
data: {
ignoreState: true,
pageTitle: 'Not Found'
ignoreState: true
}
})
.state('bad-request', {
@@ -54,21 +53,19 @@
controller: 'ErrorController',
controllerAs: 'vm',
params: {
message: function($stateParams) {
message: function ($stateParams) {
return $stateParams.message;
}
},
data: {
ignoreState: true,
pageTitle: 'Bad Request'
ignoreState: true
}
})
.state('forbidden', {
url: '/forbidden',
templateUrl: '/modules/core/client/views/403.client.view.html',
data: {
ignoreState: true,
pageTitle: 'Forbidden'
ignoreState: true
}
});
}

View File

@@ -19,13 +19,17 @@
function listener(event, toState) {
var applicationCoreTitle = 'CHD.im',
separeteBy = ' - ';
separator = ' - ',
stateTitle = applicationCoreTitle + separator;
toState.name.split('.').forEach(function (value, index) {
stateTitle = stateTitle + value.charAt(0).toUpperCase() + value.slice(1) + separator;
});
if (toState.data && toState.data.pageTitle) {
var stateTitle = $interpolate(toState.data.pageTitle)($state.$current.locals.globals);
element.html(applicationCoreTitle + separeteBy + $translate.instant(stateTitle));
} else {
element.html(applicationCoreTitle);
stateTitle = $interpolate(stateTitle + $translate.instant(toState.data.pageTitle) + separator)(($state.$current.locals.globals));
}
stateTitle = stateTitle.slice(0, 0 - separator.length);
element.text(stateTitle);
}
}
}

View File

@@ -43,11 +43,11 @@
// Add menu item object
function addMenuItem(menuId, options) {
options = options || {};
// Validate that the menu exists
service.validateMenuExistence(menuId);
options = options || {};
// Push new menu item
service.menus[menuId].items.push({
title: options.title || '',
@@ -64,11 +64,9 @@
// Add submenu items
if (options.items) {
for (var i in options.items) {
if (options.items.hasOwnProperty(i)) {
service.addSubMenuItem(menuId, options.state, options.items[i]);
}
}
options.items.forEach(function (subMenuItem) {
service.addSubMenuItem(menuId, options.state, subMenuItem);
});
}
// Return the menu object
@@ -83,21 +81,20 @@
service.validateMenuExistence(menuId);
// Search for menu item
for (var itemIndex in service.menus[menuId].items) {
if (service.menus[menuId].items[itemIndex].state === parentItemState) {
// Push new submenu item
service.menus[menuId].items[itemIndex].items.push({
title: options.title || '',
state: options.state || '',
params: options.params || {},
roles: ((options.roles === null || typeof options.roles === 'undefined') ? service.menus[menuId].items[itemIndex].roles : options.roles),
position: options.position || 0,
shouldRender: shouldRender,
target: options.target || undefined,
divider: options.divider || false
});
}
}
service.menus[menuId].items.filter(function (item) {
return item.state === parentItemState;
}).forEach(function (item) {
item.items.push({
title: options.title || '',
state: options.state || '',
params: options.params || {},
roles: ((options.roles === null || typeof options.roles === 'undefined') ? item.roles : options.roles),
position: options.position || 0,
shouldRender: shouldRender,
target: options.target || undefined,
divider: options.divider || false
});
});
// Return the menu object
return service.menus[menuId];
@@ -117,23 +114,17 @@
shouldRender = function (user) {
if (this.roles.indexOf('*') !== -1) {
return true;
} else {
if (!user) {
return false;
}
for (var userRoleIndex in user.roles) {
if (user.roles.hasOwnProperty(userRoleIndex)) {
for (var roleIndex in this.roles) {
if (this.roles.hasOwnProperty(roleIndex) && this.roles[roleIndex] === user.roles[userRoleIndex]) {
return true;
}
}
}
}
}
return false;
if (!user) {
return false;
}
var matchingRoles = user.roles.filter(function (userRole) {
return this.roles.indexOf(userRole) !== -1;
}, this);
return matchingRoles.length > 0;
};
// Adding the topbar menu
@@ -155,48 +146,40 @@
// Validate that the menu exists
service.validateMenuExistence(menuId);
// Search for menu item to remove
for (var itemIndex in service.menus[menuId].items) {
if (service.menus[menuId].items.hasOwnProperty(itemIndex) && service.menus[menuId].items[itemIndex].state === menuItemState) {
service.menus[menuId].items.splice(itemIndex, 1);
}
}
// Filter out menu items that do not match the current menu item state.
service.menus[menuId].items = service.menus[menuId].items.filter(function (item) {
return item.state !== menuItemState;
});
// Return the menu object
return service.menus[menuId];
}
// Remove existing menu object by menu id
function removeSubMenuItem(menuId, submenuItemState) {
function removeSubMenuItem(menuId, subMenuItemState) {
// Validate that the menu exists
service.validateMenuExistence(menuId);
// Search for menu item to remove
for (var itemIndex in service.menus[menuId].items) {
if (this.menus[menuId].items.hasOwnProperty(itemIndex)) {
for (var subitemIndex in service.menus[menuId].items[itemIndex].items) {
if (this.menus[menuId].items[itemIndex].items.hasOwnProperty(subitemIndex) && service.menus[menuId].items[itemIndex].items[subitemIndex].state === submenuItemState) {
service.menus[menuId].items[itemIndex].items.splice(subitemIndex, 1);
}
}
}
}
// Filter out sub-menu items that do not match the current subMenuItemState
service.menus[menuId].items.forEach(function (parentMenuItem) {
parentMenuItem.items = parentMenuItem.items.filter(function (subMenuItem) {
return subMenuItem.state !== subMenuItemState;
});
});
// Return the menu object
return service.menus[menuId];
}
// Validate menu existance
// Validate menu existence
function validateMenuExistence(menuId) {
if (menuId && menuId.length) {
if (service.menus[menuId]) {
return true;
} else {
throw new Error('Menu does not exist');
}
} else {
if (!(menuId && menuId.length)) {
throw new Error('MenuId was not provided');
}
if (!service.menus[menuId]) {
throw new Error('Menu does not exist');
}
return true;
}
}
}());

View File

@@ -1,7 +1,7 @@
'use strict';
(function() {
describe('authInterceptor', function() {
(function () {
describe('authInterceptor', function () {
// Initialize global variables
var authInterceptor,
$q,
@@ -13,11 +13,11 @@
beforeEach(module(ApplicationConfiguration.applicationModuleName));
// Load httpProvider
beforeEach(module(function($httpProvider) {
beforeEach(module(function ($httpProvider) {
httpProvider = $httpProvider;
}));
beforeEach(inject(function(_authInterceptor_, _$q_, _$state_, _Authentication_) {
beforeEach(inject(function (_authInterceptor_, _$q_, _$state_, _Authentication_) {
authInterceptor = _authInterceptor_;
$q = _$q_;
$state = _$state_;
@@ -26,19 +26,19 @@
spyOn($state, 'transitionTo');
}));
it('Auth Interceptor should be object', function() {
it('Auth Interceptor should be object', function () {
expect(typeof authInterceptor).toEqual('object');
});
it('Auth Interceptor should contain responseError function', function() {
it('Auth Interceptor should contain responseError function', function () {
expect(typeof authInterceptor.responseError).toEqual('function');
});
it('httpProvider Interceptor should have authInterceptor', function() {
it('httpProvider Interceptor should have authInterceptor', function () {
expect(httpProvider.interceptors).toContain('authInterceptor');
});
describe('Forbidden Interceptor', function() {
describe('Forbidden Interceptor', function () {
it('should redirect to forbidden route', function () {
var response = {
status: 403,
@@ -50,7 +50,7 @@
});
});
describe('Authorization Interceptor', function() {
describe('Authorization Interceptor', function () {
it('should redirect to signIn page for unauthorized access', function () {
var response = {
status: 401,
@@ -63,9 +63,9 @@
});
});
describe('Unresponsive Interceptor', function() {
describe('Unresponsive Interceptor', function () {
var Notification;
beforeEach(inject(function(_Notification_) {
beforeEach(inject(function (_Notification_) {
Notification = _Notification_;
spyOn(Notification, 'error');
}));

View File

@@ -1,7 +1,7 @@
'use strict';
(function() {
describe('Menus', function() {
(function () {
describe('Menus', function () {
// Initialize global variables
var scope,
menuService;
@@ -9,78 +9,78 @@
// Load the main application module
beforeEach(module(ApplicationConfiguration.applicationModuleName));
beforeEach(inject(function(_menuService_) {
beforeEach(inject(function (_menuService_) {
menuService = _menuService_;
}));
it('should have topbar added', function() {
it('should have topbar added', function () {
expect(menuService.menus.topbar).toBeDefined();
});
it('should have default roles to user and admin', function() {
it('should have default roles to user and admin', function () {
expect(menuService.defaultRoles).toEqual(['user', 'admin']);
});
describe('addMenu', function() {
describe('with no options', function() {
describe('addMenu', function () {
describe('with no options', function () {
var menuId = 'menu1',
menu;
beforeEach(function() {
beforeEach(function () {
menu = menuService.addMenu(menuId);
});
it('should return menu object', function() {
it('should return menu object', function () {
expect(menu).toBeDefined();
});
it('should default roles', function() {
it('should default roles', function () {
expect(menu.roles).toEqual(menuService.defaultRoles);
});
it('should have empty items', function() {
it('should have empty items', function () {
expect(menu.items).toEqual([]);
});
it('should set shouldRender to shouldRender function handle', function() {
it('should set shouldRender to shouldRender function handle', function () {
expect(menu.shouldRender()).toBeFalsy();
});
});
describe('with options', function() {
describe('with options', function () {
var menu,
options = {
roles: ['a', 'b', 'c'],
items: ['d', 'e', 'f']
};
beforeEach(function() {
beforeEach(function () {
menu = menuService.addMenu('menu1', options);
});
it('should set items to options.items list', function() {
it('should set items to options.items list', function () {
expect(menu.items).toBe(options.items);
});
it('should set roles to options.roles list', function() {
it('should set roles to options.roles list', function () {
expect(menu.roles).toBe(options.roles);
});
});
});
describe('shouldRender', function() {
describe('shouldRender', function () {
var menuOptions = {
roles: ['*', 'menurole']
},
menu;
beforeEach(function() {
beforeEach(function () {
menu = menuService.addMenu('menu1', menuOptions);
});
describe('when logged out', function() {
it('should render if menu is public', function() {
describe('when logged out', function () {
it('should render if menu is public', function () {
expect(menu.shouldRender()).toBeTruthy();
});
it('should not render if menu is private', function() {
it('should not render if menu is private', function () {
menu = menuService.addMenu('menu1', {
isPublic: false
});
@@ -88,28 +88,28 @@
});
});
describe('when logged in', function() {
describe('when logged in', function () {
var user = {
roles: ['1', 'menurole', '2']
};
describe('menu with * role', function() {
it('should render', function() {
describe('menu with * role', function () {
it('should render', function () {
expect(menu.shouldRender(user)).toBeTruthy();
});
});
describe('menu without * role', function() {
beforeEach(function() {
describe('menu without * role', function () {
beforeEach(function () {
menu = menuService.addMenu('menu1', {
roles: ['b', 'menurole', 'c']
});
});
it('should render if user has same role as menu', function() {
it('should render if user has same role as menu', function () {
expect(menu.shouldRender(user)).toBeTruthy();
});
it('should not render if user has different roles', function() {
it('should not render if user has different roles', function () {
user = {
roles: ['1', '2', '3']
};
@@ -119,54 +119,54 @@
});
});
describe('validateMenuExistence', function() {
describe('when menuId not provided', function() {
it('should throw menuId error', function() {
describe('validateMenuExistence', function () {
describe('when menuId not provided', function () {
it('should throw menuId error', function () {
expect(menuService.validateMenuExistence).toThrowError('MenuId was not provided');
});
});
describe('when menu does not exist', function() {
it('should throw no menu error', function() {
var target = function() {
describe('when menu does not exist', function () {
it('should throw no menu error', function () {
var target = function () {
menuService.validateMenuExistence('noMenuId');
};
expect(target).toThrowError('Menu does not exist');
});
});
describe('when menu exists', function() {
describe('when menu exists', function () {
var menuId = 'menuId';
beforeEach(function() {
beforeEach(function () {
menuService.menus[menuId] = {};
});
it('should return truthy', function() {
it('should return truthy', function () {
expect(menuService.validateMenuExistence(menuId)).toBeTruthy();
});
});
});
describe('removeMenu', function() {
describe('removeMenu', function () {
var menu = {
id: 'menuId'
};
beforeEach(function() {
beforeEach(function () {
menuService.menus[menu.id] = menu;
menuService.validateMenuExistence = jasmine.createSpy();
menuService.removeMenu(menu.id);
});
it('should remove existing menu from menus', function() {
it('should remove existing menu from menus', function () {
expect(menuService.menus).not.toContain(menu.id);
});
it('validates menu existance before removing', function() {
it('validates menu existance before removing', function () {
expect(menuService.validateMenuExistence).toHaveBeenCalledWith(menu.id);
});
});
describe('addMenuItem', function() {
describe('addMenuItem', function () {
var menuId = 'menu1',
subMenuItem1 = {
title: 'sub1'
@@ -187,7 +187,7 @@
menu,
menuItem;
beforeEach(function() {
beforeEach(function () {
menuService.validateMenuExistence = jasmine.createSpy();
menuService.addSubMenuItem = jasmine.createSpy();
menuService.addMenu(menuId, {
@@ -197,84 +197,84 @@
menuItem = menu.items[0];
});
it('should validate menu existance', function() {
it('should validate menu existance', function () {
expect(menuService.validateMenuExistence).toHaveBeenCalledWith(menuId);
});
it('should return the menu', function() {
it('should return the menu', function () {
expect(menu).toBeDefined();
});
it('should set menu item shouldRender function', function() {
it('should set menu item shouldRender function', function () {
expect(menuItem.shouldRender).toBeDefined();
});
describe('with options set', function() {
it('should add menu item to menu', function() {
describe('with options set', function () {
it('should add menu item to menu', function () {
expect(menu.items.length).toBe(1);
});
it('should set menu item title to options title', function() {
it('should set menu item title to options title', function () {
expect(menuItem.title).toBe(menuItemOptions.title);
});
it('should set menu item state to options state', function() {
it('should set menu item state to options state', function () {
expect(menuItem.state).toBe(menuItemOptions.state);
});
it('should set menu item type to options type', function() {
it('should set menu item type to options type', function () {
expect(menuItem.type).toBe(menuItemOptions.type);
});
it('should set menu item class to options class', function() {
it('should set menu item class to options class', function () {
expect(menuItem.class).toBe(menuItemOptions.class);
});
it('should set menu item position to options position', function() {
it('should set menu item position to options position', function () {
expect(menuItem.position).toBe(menuItemOptions.position);
});
it('should call addSubMenuItem for each item in options', function() {
it('should call addSubMenuItem for each item in options', function () {
expect(menuService.addSubMenuItem).toHaveBeenCalledWith(menuId, menuItemOptions.state, subMenuItem1);
expect(menuService.addSubMenuItem).toHaveBeenCalledWith(menuId, menuItemOptions.state, subMenuItem2);
});
});
describe('without options set', function() {
beforeEach(function() {
describe('without options set', function () {
beforeEach(function () {
menu = menuService.addMenuItem(menuId);
menuItem = menu.items[1];
});
it('should set menu item type to item', function() {
it('should set menu item type to item', function () {
expect(menuItem.type).toBe('item');
});
it('should set menu item title to empty', function() {
it('should set menu item title to empty', function () {
expect(menuItem.title).toBe('');
});
it('should set menu item isPublic to false', function() {
it('should set menu item isPublic to false', function () {
expect(menuItem.isPublic).toBeFalsy();
});
it('should set menu item roles to default roles', function() {
it('should set menu item roles to default roles', function () {
expect(menuItem.roles).toEqual(menuService.defaultRoles);
});
it('should set menu item position to 0', function() {
it('should set menu item position to 0', function () {
expect(menuItem.position).toBe(0);
});
});
});
describe('removeMenuItem', function() {
describe('removeMenuItem', function () {
var menuId = 'menuId',
menuItemState = 'menu.state1',
menuItemState2 = 'menu.state2',
menu;
beforeEach(function() {
beforeEach(function () {
menuService.addMenu(menuId);
menuService.addMenuItem(menuId, { state: menuItemState });
menuService.addMenuItem(menuId, { state: menuItemState2 });
@@ -282,21 +282,21 @@
menu = menuService.removeMenuItem(menuId, menuItemState);
});
it('should return menu object', function() {
it('should return menu object', function () {
expect(menu).not.toBeNull();
});
it('should validate menu existance', function() {
it('should validate menu existance', function () {
expect(menuService.validateMenuExistence).toHaveBeenCalledWith(menuId);
});
it('should remove sub menu items with same state', function() {
it('should remove sub menu items with same state', function () {
expect(menu.items.length).toBe(1);
expect(menu.items[0].state).toBe(menuItemState2);
});
});
describe('addSubMenuItem', function() {
describe('addSubMenuItem', function () {
var subItemOptions = {
title: 'title',
state: 'sub.state',
@@ -324,7 +324,7 @@
subItem2,
menu;
beforeEach(function() {
beforeEach(function () {
menuService.validateMenuExistence = jasmine.createSpy();
menuService.addMenu(menuId);
menuService.addMenuItem(menuId, menuItem1Options);
@@ -339,93 +339,93 @@
subItem2 = menuItem1.items[1];
});
afterEach(function() {
afterEach(function () {
menuService.removeMenu(menuId);
});
it('should return menu object', function() {
it('should return menu object', function () {
expect(menu).not.toBeNull();
});
it('should validate menu existance', function() {
it('should validate menu existance', function () {
expect(menuService.validateMenuExistence).toHaveBeenCalledWith(menuId);
});
it('should not add sub menu item to menu item of different state', function() {
it('should not add sub menu item to menu item of different state', function () {
expect(menuItem3.items.length).toBe(0);
});
it('should set shouldRender', function() {
it('should set shouldRender', function () {
expect(subItem1.shouldRender).toBeDefined();
});
describe('with options set', function() {
it('should add sub menu item to menu item', function() {
describe('with options set', function () {
it('should add sub menu item to menu item', function () {
expect(subItem1).toBeDefined();
});
it('should set title to options title', function() {
it('should set title to options title', function () {
expect(subItem1.title).toBe(subItemOptions.title);
});
it('should set state to options state', function() {
it('should set state to options state', function () {
expect(subItem1.state).toBe(subItemOptions.state);
});
it('should set roles to options roles', function() {
it('should set roles to options roles', function () {
expect(subItem1.roles).toEqual(subItemOptions.roles);
});
it('should set position to options position', function() {
it('should set position to options position', function () {
expect(subItem1.position).toEqual(subItemOptions.position);
});
it('should set params to options params', function() {
it('should set params to options params', function () {
expect(subItem1.params).toEqual(subItemOptions.params);
});
});
describe('without optoins set', function() {
it('should add sub menu item to menu item', function() {
describe('without optoins set', function () {
it('should add sub menu item to menu item', function () {
expect(subItem2).toBeDefined();
});
it('should set isPublic to parent isPublic', function() {
it('should set isPublic to parent isPublic', function () {
expect(subItem2.isPublic).toBe(menuItem1.isPublic);
});
it('should set title to blank', function() {
it('should set title to blank', function () {
expect(subItem2.title).toBe('');
});
it('should set state to blank', function() {
it('should set state to blank', function () {
expect(subItem2.state).toBe('');
});
it('should set roles to parent roles', function() {
it('should set roles to parent roles', function () {
expect(subItem2.roles).toEqual(menuItem1.roles);
});
it('should set position to 0', function() {
it('should set position to 0', function () {
expect(subItem2.position).toBe(0);
});
});
describe('then removeSubMenuItem', function() {
beforeEach(function() {
describe('then removeSubMenuItem', function () {
beforeEach(function () {
menuService.validateMenuExistence = jasmine.createSpy();
menu = menuService.removeSubMenuItem(menuId, subItem1.state);
});
it('should validate menu existance', function() {
it('should validate menu existance', function () {
expect(menuService.validateMenuExistence).toHaveBeenCalledWith(menuId);
});
it('should return menu object', function() {
it('should return menu object', function () {
expect(menu).toBeDefined();
});
it('should remove sub menu item', function() {
it('should remove sub menu item', function () {
expect(menuItem1.items.length).toBe(1);
expect(menuItem1.items[0].state).toEqual(subItem2.state);
});

View File

@@ -1,4 +1,4 @@
(function() {
(function () {
'use strict';
/* Creates a mock of socket.io for the browser.

File diff suppressed because it is too large Load Diff

View File

@@ -6,12 +6,12 @@
autocomplete="off">
<fieldset>
<div class="form-group" show-errors>
<input type="text" id="username" name="username" class="form-control" ng-model="vm.credentials.username"
<input type="text" id="usernameOrEmail" name="usernameOrEmail" class="form-control" ng-model="vm.credentials.usernameOrEmail"
placeholder="{{ 'SIGN.ENTER_USERNAME' | translate}}"
lowercase required autofocus>
<div ng-messages="vm.forgotPasswordForm.username.$error" role="alert">
<p class="help-block error-text" ng-message="required">Enter a username.</p>
<div ng-messages="vm.forgotPasswordForm.usernameOrEmail.$error" role="alert">
<p class="help-block error-text" ng-message="required">Enter a username or email.</p>
</div>
</div>
<div class="text-center form-group">
@@ -21,4 +21,3 @@
</form>
</div>
</div>

View File

@@ -11,7 +11,7 @@ var passport = require('passport'),
/**
* Module init function
*/
module.exports = function (app, db) {
module.exports = function (app) {
// Serialize sessions
passport.serializeUser(function (user, done) {
user.updateSignedTime();

View File

@@ -31,17 +31,23 @@ exports.forgot = function (req, res, next) {
},
// Lookup user by username
function (token, done) {
if (req.body.username) {
if (req.body.usernameOrEmail) {
var usernameOrEmail = String(req.body.usernameOrEmail).toLowerCase();
User.findOne({
username: req.body.username.toLowerCase()
$or: [
{ username: usernameOrEmail },
{ email: usernameOrEmail }
]
}, '-salt -password', function (err, user) {
if (err || !user) {
return res.status(400).send({
message: 'No account with that username has been found'
message: 'No account with that username or email has been found'
});
} else if (user.provider !== 'local') {
return res.status(400).send({
message: 'It seems like you signed up using your ' + user.provider + ' account'
message: 'It seems like you signed up using your ' + user.provider + ' account, please sign in using that provider.'
});
} else {
user.resetPasswordToken = token;
@@ -54,7 +60,7 @@ exports.forgot = function (req, res, next) {
});
} else {
return res.status(422).send({
message: 'Username field must not be blank'
message: 'Username/email field must not be blank'
});
}
},

View File

@@ -81,7 +81,7 @@ exports.changeProfilePicture = function (req, res) {
});
}
function uploadImage () {
function uploadImage() {
return new Promise(function (resolve, reject) {
upload(req, res, function (uploadError) {
if (uploadError) {
@@ -93,7 +93,7 @@ exports.changeProfilePicture = function (req, res) {
});
}
function updateUser () {
function updateUser() {
return new Promise(function (resolve, reject) {
user.profileImageURL = config.uploads.profile.image.dest + req.file.filename;
user.save(function (err, theuser) {
@@ -106,12 +106,20 @@ exports.changeProfilePicture = function (req, res) {
});
}
function deleteOldImage () {
function deleteOldImage() {
return new Promise(function (resolve, reject) {
if (existingImageUrl !== User.schema.path('profileImageURL').defaultValue) {
fs.unlink(existingImageUrl, function (unlinkError) {
if (unlinkError) {
console.log(unlinkError);
// If file didn't exist, no need to reject promise
if (unlinkError.code === 'ENOENT') {
console.log('Removing profile image failed because file did not exist.');
return resolve();
}
console.error(unlinkError);
reject({
message: 'Error occurred while deleting old profile picture'
});
@@ -125,7 +133,7 @@ exports.changeProfilePicture = function (req, res) {
});
}
function login () {
function login() {
return new Promise(function (resolve, reject) {
req.login(user, function (err) {
if (err) {

View File

@@ -11,7 +11,8 @@ var mongoose = require('mongoose'),
validator = require('validator'),
generatePassword = require('generate-password'),
owasp = require('owasp-password-strength-test'),
moment = require('moment');
moment = require('moment'),
chalk = require('chalk');
owasp.config(config.shared.owasp);
@@ -424,4 +425,92 @@ UserSchema.statics.generateRandomPassphrase = function () {
});
};
UserSchema.statics.seed = seed;
mongoose.model('User', UserSchema);
/**
* Seeds the User collection with document (User)
* and provided options.
*/
function seed(doc, options) {
var User = mongoose.model('User');
return new Promise(function (resolve, reject) {
skipDocument()
.then(add)
.then(function (response) {
return resolve(response);
})
.catch(function (err) {
return reject(err);
});
function skipDocument() {
return new Promise(function (resolve, reject) {
User
.findOne({
username: doc.username
})
.exec(function (err, existing) {
if (err) {
return reject(err);
}
if (!existing) {
return resolve(false);
}
if (existing && !options.overwrite) {
return resolve(true);
}
// Remove User (overwrite)
existing.remove(function (err) {
if (err) {
return reject(err);
}
return resolve(false);
});
});
});
}
function add(skip) {
return new Promise(function (resolve, reject) {
if (skip) {
return resolve({
message: chalk.yellow('Database Seeding: User\t\t' + doc.username + ' skipped')
});
}
User.generateRandomPassphrase()
.then(function (passphrase) {
var user = new User(doc);
user.provider = 'local';
user.displayName = user.firstName + ' ' + user.lastName;
user.password = passphrase;
user.save(function (err) {
if (err) {
return reject(err);
}
return resolve({
message: 'Database Seeding: User\t\t' + user.username + ' added with password set to ' + passphrase
});
});
})
.catch(function (err) {
return reject(err);
});
});
}
});
}

View File

@@ -1,8 +1,8 @@
'use strict';
(function() {
(function () {
// Password Validator Directive Spec
describe('PasswordValidatorDirective', function() {
describe('PasswordValidatorDirective', function () {
// Initialize global variables
var scope,
element,
@@ -12,7 +12,7 @@
// Load the main application module
beforeEach(module(ApplicationConfiguration.applicationModuleName));
beforeEach(inject(function(_$rootScope_, _$compile_) {
beforeEach(inject(function (_$rootScope_, _$compile_) {
// Set a new global scope
scope = _$rootScope_.$new();
$compile = _$compile_;
@@ -30,7 +30,7 @@
// inject allows you to use AngularJS dependency injection
// to retrieve and use other services
inject(function($compile) {
inject(function ($compile) {
var form = $compile(template)(scope);
element = form.find('div');
@@ -39,7 +39,7 @@
});
}
describe('Initialize', function() {
describe('Initialize', function () {
beforeEach(function () {
compileDirective();
});
@@ -65,7 +65,7 @@
expect(scope.requirementsProgress).toEqual(undefined);
});
it('should be valid when password meets requirements - "P@ssw0rd!!""', function() {
it('should be valid when password meets requirements - "P@ssw0rd!!""', function () {
scope.passwordMock.password = 'P@ssw0rd!!';
compileDirective();
scope.$digest();
@@ -76,7 +76,7 @@
expect(scope.requirementsProgress).toEqual('100');
});
it('should be valid when password meets requirements with a passphrase', function() {
it('should be valid when password meets requirements with a passphrase', function () {
scope.passwordMock.password = 'Open-Source Full-Stack Solution for MEAN';
compileDirective();
scope.$digest();
@@ -87,7 +87,7 @@
expect(scope.requirementsProgress).toEqual('100');
});
it('should not allow a less than 10 characters long - "P@$$w0rd!"', function() {
it('should not allow a less than 10 characters long - "P@$$w0rd!"', function () {
scope.passwordMock.password = 'P@$$w0rd!';
compileDirective();
scope.$digest();
@@ -99,7 +99,7 @@
expect(scope.requirementsProgress).toEqual('80');
});
it('should not allow a greater than 128 characters long', function() {
it('should not allow a greater than 128 characters long', function () {
scope.passwordMock.password = ')!/uLT="lh&:`6X!]|15o!$!TJf,.13l?vG].-j],lFPe/QhwN#{Z<[*1nX@n1^?WW-%_.*D)m$toB+N7z}kcN#B_d(f41h%w@0F!]igtSQ1gl~6sEV&r~}~1ub>If1c+';
compileDirective();
scope.$digest();
@@ -111,7 +111,7 @@
expect(scope.requirementsProgress).toEqual('80');
});
it('should not allow more than 3 or more repeating characters - "P@$$w0rd!!!"', function() {
it('should not allow more than 3 or more repeating characters - "P@$$w0rd!!!"', function () {
scope.passwordMock.password = 'P@$$w0rd!!!';
compileDirective();
scope.$digest();
@@ -123,7 +123,7 @@
expect(scope.requirementsProgress).toEqual('80');
});
it('should not allow a password with no uppercase letters - "p@$$w0rd!!"', function() {
it('should not allow a password with no uppercase letters - "p@$$w0rd!!"', function () {
scope.passwordMock.password = 'p@$$w0rd!!';
compileDirective();
scope.$digest();
@@ -135,7 +135,7 @@
expect(scope.requirementsProgress).toEqual('80');
});
it('should not allow a password with less than one number - "P@$$word!!"', function() {
it('should not allow a password with less than one number - "P@$$word!!"', function () {
scope.passwordMock.password = 'P@$$word!!';
compileDirective();
scope.$digest();
@@ -147,7 +147,7 @@
expect(scope.requirementsProgress).toEqual('80');
});
it('should not allow a password with less than one special character - "Passw0rdss"', function() {
it('should not allow a password with less than one special character - "Passw0rdss"', function () {
scope.passwordMock.password = 'Passw0rdss';
compileDirective();
scope.$digest();
@@ -159,7 +159,7 @@
expect(scope.requirementsProgress).toEqual('80');
});
it('should show 20% progress and "danger" color', function() {
it('should show 20% progress and "danger" color', function () {
scope.passwordMock.password = 'P';
compileDirective();
scope.$digest();
@@ -168,7 +168,7 @@
expect(scope.requirementsProgress).toEqual('20');
});
it('should show 40% progress and "warning" color', function() {
it('should show 40% progress and "warning" color', function () {
scope.passwordMock.password = 'Pa';
compileDirective();
scope.$digest();
@@ -177,7 +177,7 @@
expect(scope.requirementsProgress).toEqual('40');
});
it('should show 60% progress and "info" color', function() {
it('should show 60% progress and "info" color', function () {
scope.passwordMock.password = 'Pa$';
compileDirective();
scope.$digest();
@@ -186,7 +186,7 @@
expect(scope.requirementsProgress).toEqual('60');
});
it('should show 80% progress and "primary" color', function() {
it('should show 80% progress and "primary" color', function () {
scope.passwordMock.password = 'Pa$$w0rd';
compileDirective();
scope.$digest();
@@ -195,7 +195,7 @@
expect(scope.requirementsProgress).toEqual('80');
});
it('should show 100% progress and "success" color', function() {
it('should show 100% progress and "success" color', function () {
scope.passwordMock.password = 'Pa$$w0rd!!';
compileDirective();
scope.$digest();

View File

@@ -1,8 +1,8 @@
'use strict';
(function() {
(function () {
// Password Verify Directive Spec
describe('PasswordVerifyDirective', function() {
describe('PasswordVerifyDirective', function () {
// Initialize global variables
var scope,
element,
@@ -12,7 +12,7 @@
// Load the main application module
beforeEach(module(ApplicationConfiguration.applicationModuleName));
beforeEach(inject(function(_$rootScope_, _$compile_) {
beforeEach(inject(function (_$rootScope_, _$compile_) {
// Set a new global scope
scope = _$rootScope_.$new();
$compile = _$compile_;
@@ -32,7 +32,7 @@
// inject allows you to use AngularJS dependency injection
// to retrieve and use other services
inject(function($compile) {
inject(function ($compile) {
var form = $compile(template)(scope);
element = form.find('div');
@@ -41,7 +41,7 @@
});
}
describe('Initialize', function() {
describe('Initialize', function () {
beforeEach(function () {
compileDirective();
});

View File

@@ -1,8 +1,8 @@
'use strict';
(function() {
(function () {
// Password controller Spec
describe('PasswordController', function() {
describe('PasswordController', function () {
// Initialize global variables
var PasswordController,
scope,
@@ -12,11 +12,11 @@
$window,
Notification;
beforeEach(function() {
beforeEach(function () {
jasmine.addMatchers({
toEqualData: function(util, customEqualityTesters) {
toEqualData: function (util, customEqualityTesters) {
return {
compare: function(actual, expected) {
compare: function (actual, expected) {
return {
pass: angular.equals(actual, expected)
};
@@ -29,8 +29,8 @@
// Load the main application module
beforeEach(module(ApplicationConfiguration.applicationModuleName));
describe('Logged in user', function() {
beforeEach(inject(function($controller, $rootScope, _UsersService_, _Authentication_, _$stateParams_, _$httpBackend_, _$location_) {
describe('Logged in user', function () {
beforeEach(inject(function ($controller, $rootScope, _UsersService_, _Authentication_, _$stateParams_, _$httpBackend_, _$location_) {
// Set a new global scope
scope = $rootScope.$new();
@@ -55,13 +55,13 @@
});
}));
it('should redirect logged in user to home', function() {
it('should redirect logged in user to home', function () {
expect($location.path).toHaveBeenCalledWith('/');
});
});
describe('Logged out user', function() {
beforeEach(inject(function($controller, $rootScope, _$window_, _$stateParams_, _$httpBackend_, _$location_, _Notification_) {
describe('Logged out user', function () {
beforeEach(inject(function ($controller, $rootScope, _$window_, _$stateParams_, _$httpBackend_, _$location_, _Notification_) {
// Set a new global scope
scope = $rootScope.$new();
@@ -87,22 +87,22 @@
});
}));
it('should not redirect to home', function() {
it('should not redirect to home', function () {
expect($location.path).not.toHaveBeenCalledWith('/');
});
describe('askForPasswordReset', function() {
describe('askForPasswordReset', function () {
var credentials = {
username: 'test',
password: 'P@ssw0rd!!'
};
beforeEach(function() {
beforeEach(function () {
scope.vm.credentials = credentials;
});
describe('POST error', function() {
describe('POST error', function () {
var errorMessage = 'No account with that username has been found';
beforeEach(function() {
beforeEach(function () {
$httpBackend.when('POST', '/api/auth/forgot', credentials).respond(400, {
'message': errorMessage
});
@@ -111,18 +111,18 @@
$httpBackend.flush();
});
it('should clear form', function() {
it('should clear form', function () {
expect(scope.vm.credentials).toBe(null);
});
it('should call Notification.error with response message', function() {
it('should call Notification.error with response message', function () {
expect(Notification.error).toHaveBeenCalledWith({ message: errorMessage, title: '<i class="glyphicon glyphicon-remove"></i> Failed to send password reset email!', delay: 4000 });
});
});
describe('POST success', function() {
describe('POST success', function () {
var successMessage = 'An email has been sent to the provided email with further instructions.';
beforeEach(function() {
beforeEach(function () {
$httpBackend.when('POST', '/api/auth/forgot', credentials).respond({
'message': successMessage
});
@@ -131,27 +131,27 @@
$httpBackend.flush();
});
it('should clear form', function() {
it('should clear form', function () {
expect(scope.vm.credentials).toBe(null);
});
it('should call Notification.success with response message', function() {
it('should call Notification.success with response message', function () {
expect(Notification.success).toHaveBeenCalledWith({ message: successMessage, title: '<i class="glyphicon glyphicon-ok"></i> Password reset email sent successfully!' });
});
});
});
describe('resetUserPassword', function() {
describe('resetUserPassword', function () {
var token = 'testToken';
var passwordDetails = {
password: 'test'
};
beforeEach(function() {
beforeEach(function () {
$stateParams.token = token;
scope.vm.passwordDetails = passwordDetails;
});
it('POST error should call Notification.error with response message', function() {
it('POST error should call Notification.error with response message', function () {
var errorMessage = 'Passwords do not match';
$httpBackend.when('POST', '/api/auth/reset/' + token, passwordDetails).respond(400, {
'message': errorMessage
@@ -163,26 +163,26 @@
expect(Notification.error).toHaveBeenCalledWith({ message: errorMessage, title: '<i class="glyphicon glyphicon-remove"></i> Password reset failed!', delay: 4000 });
});
describe('POST success', function() {
describe('POST success', function () {
var user = {
username: 'test'
};
beforeEach(function() {
beforeEach(function () {
$httpBackend.when('POST', '/api/auth/reset/' + token, passwordDetails).respond(user);
scope.vm.resetUserPassword(true);
$httpBackend.flush();
});
it('should clear password form', function() {
it('should clear password form', function () {
expect(scope.vm.passwordDetails).toBe(null);
});
it('should attach user profile', function() {
it('should attach user profile', function () {
expect(scope.vm.authentication.user.username).toEqual(user.username);
});
it('should redirect to password reset success view with Notification.success', function() {
it('should redirect to password reset success view with Notification.success', function () {
expect(Notification.success).toHaveBeenCalledWith({ message: '<i class="glyphicon glyphicon-ok"></i> Password reset successful!' });
expect($location.path).toHaveBeenCalledWith('/password/reset/success');
});

View File

@@ -421,7 +421,7 @@ describe('Users E2E Tests:', function () {
expect(element.all(by.css('.error-text')).get(1).getText()).toBe('Password is required.');
});
it('Verify that the user is logged in', function() {
it('Verify that the user is logged in', function () {
// Make sure user is signed out first
signout();
// Sign in

View File

@@ -186,7 +186,7 @@ describe('User Model Unit Tests:', function () {
_user3.email = _user1.email;
_user3.save(function (err) {
should.exist(err);
_user1.remove(function(err) {
_user1.remove(function (err) {
should.not.exist(err);
done();
});
@@ -224,7 +224,7 @@ describe('User Model Unit Tests:', function () {
_user1.save(function (err) {
should.not.exist(err);
_user1.password.should.not.equal(passwordBeforeSave);
_user1.remove(function(err) {
_user1.remove(function (err) {
should.not.exist(err);
done();
});
@@ -238,7 +238,7 @@ describe('User Model Unit Tests:', function () {
_user1.save(function (err) {
should.not.exist(err);
_user1.password.should.not.equal(passwordBeforeSave);
_user1.remove(function(err) {
_user1.remove(function (err) {
should.not.exist(err);
done();
});
@@ -246,7 +246,7 @@ describe('User Model Unit Tests:', function () {
});
});
describe('User Password Validation Tests', function() {
describe('User Password Validation Tests', function () {
it('should validate when the password strength passes - "P@$$w0rd!!"', function () {
var _user1 = new User(user1);
_user1.password = 'P@$$w0rd!!';
@@ -351,7 +351,7 @@ describe('User Model Unit Tests:', function () {
});
});
describe('User E-mail Validation Tests', function() {
describe('User E-mail Validation Tests', function () {
it('should not allow invalid email address - "123"', function (done) {
var _user1 = new User(user1);
@@ -645,12 +645,12 @@ describe('User Model Unit Tests:', function () {
});
describe('Username Validation', function() {
it('should show error to save username beginning with .', function(done) {
describe('Username Validation', function () {
it('should show error to save username beginning with .', function (done) {
var _user = new User(user1);
_user.username = '.login';
_user.save(function(err) {
_user.save(function (err) {
should.exist(err);
done();
});
@@ -660,67 +660,67 @@ describe('User Model Unit Tests:', function () {
var _user = new User(user1);
_user.username = config.illegalUsernames[Math.floor(Math.random() * config.illegalUsernames.length)];
_user.save(function(err) {
_user.save(function (err) {
should.exist(err);
done();
});
});
it('should show error to save username end with .', function(done) {
it('should show error to save username end with .', function (done) {
var _user = new User(user1);
_user.username = 'login.';
_user.save(function(err) {
_user.save(function (err) {
should.exist(err);
done();
});
});
it('should show error to save username with ..', function(done) {
it('should show error to save username with ..', function (done) {
var _user = new User(user1);
_user.username = 'log..in';
_user.save(function(err) {
_user.save(function (err) {
should.exist(err);
done();
});
});
it('should show error to save username shorter than 3 character', function(done) {
it('should show error to save username shorter than 3 character', function (done) {
var _user = new User(user1);
_user.username = 'lo';
_user.save(function(err) {
_user.save(function (err) {
should.exist(err);
done();
});
});
it('should show error saving a username without at least one alphanumeric character', function(done) {
it('should show error saving a username without at least one alphanumeric character', function (done) {
var _user = new User(user1);
_user.username = '-_-';
_user.save(function(err) {
_user.save(function (err) {
should.exist(err);
done();
});
});
it('should show error saving a username longer than 34 characters', function(done) {
it('should show error saving a username longer than 34 characters', function (done) {
var _user = new User(user1);
_user.username = 'l'.repeat(35);
_user.save(function(err) {
_user.save(function (err) {
should.exist(err);
done();
});
});
it('should save username with dot', function(done) {
it('should save username with dot', function (done) {
var _user = new User(user1);
_user.username = 'log.in';
_user.save(function(err) {
_user.save(function (err) {
should.not.exist(err);
done();
});

View File

@@ -6,6 +6,7 @@ var semver = require('semver'),
path = require('path'),
mongoose = require('mongoose'),
User = mongoose.model('User'),
config = require(path.resolve('./config/config')),
express = require(path.resolve('./config/lib/express'));
/**
@@ -26,7 +27,7 @@ describe('User CRUD tests', function () {
before(function (done) {
// Get application
app = express.init(mongoose);
app = express.init(mongoose.connection.db);
agent = request.agent(app);
done();
@@ -325,7 +326,7 @@ describe('User CRUD tests', function () {
should.not.exist(err);
agent.post('/api/auth/forgot')
.send({
username: 'some_username_that_doesnt_exist'
usernameOrEmail: 'some_username_that_doesnt_exist'
})
.expect(400)
.end(function (err, res) {
@@ -334,13 +335,13 @@ describe('User CRUD tests', function () {
return done(err);
}
res.body.message.should.equal('No account with that username has been found');
res.body.message.should.equal('No account with that username or email has been found');
return done();
});
});
});
it('forgot password should return 400 for no username provided', function (done) {
it('forgot password should return 400 for empty username/email', function (done) {
var provider = 'facebook';
user.provider = provider;
user.roles = ['user'];
@@ -349,7 +350,7 @@ describe('User CRUD tests', function () {
should.not.exist(err);
agent.post('/api/auth/forgot')
.send({
username: ''
usernameOrEmail: ''
})
.expect(422)
.end(function (err, res) {
@@ -358,7 +359,29 @@ describe('User CRUD tests', function () {
return done(err);
}
res.body.message.should.equal('Username field must not be blank');
res.body.message.should.equal('Username/email field must not be blank');
return done();
});
});
});
it('forgot password should return 400 for no username or email provided', function (done) {
var provider = 'facebook';
user.provider = provider;
user.roles = ['user'];
user.save(function (err) {
should.not.exist(err);
agent.post('/api/auth/forgot')
.send({})
.expect(422)
.end(function (err, res) {
// Handle error
if (err) {
return done(err);
}
res.body.message.should.equal('Username/email field must not be blank');
return done();
});
});
@@ -373,7 +396,7 @@ describe('User CRUD tests', function () {
should.not.exist(err);
agent.post('/api/auth/forgot')
.send({
username: user.username
usernameOrEmail: user.username
})
.expect(400)
.end(function (err, res) {
@@ -382,20 +405,20 @@ describe('User CRUD tests', function () {
return done(err);
}
res.body.message.should.equal('It seems like you signed up using your ' + user.provider + ' account');
res.body.message.should.equal('It seems like you signed up using your ' + user.provider + ' account, please sign in using that provider.');
return done();
});
});
});
it('forgot password should be able to reset password for user password reset request', function (done) {
it('forgot password should be able to reset password for user password reset request using username', function (done) {
user.roles = ['user'];
user.save(function (err) {
should.not.exist(err);
agent.post('/api/auth/forgot')
.send({
username: user.username
usernameOrEmail: user.username
})
.expect(400)
.end(function (err, res) {
@@ -404,7 +427,33 @@ describe('User CRUD tests', function () {
return done(err);
}
User.findOne({ username: user.username.toLowerCase() }, function(err, userRes) {
User.findOne({ username: user.username.toLowerCase() }, function (err, userRes) {
userRes.resetPasswordToken.should.not.be.empty();
should.exist(userRes.resetPasswordExpires);
res.body.message.should.be.equal('Failure sending email');
return done();
});
});
});
});
it('forgot password should be able to reset password for user password reset request using email', function (done) {
user.roles = ['user'];
user.save(function (err) {
should.not.exist(err);
agent.post('/api/auth/forgot')
.send({
usernameOrEmail: user.email
})
.expect(400)
.end(function (err, res) {
// Handle error
if (err) {
return done(err);
}
User.findOne({ username: user.username.toLowerCase() }, function (err, userRes) {
userRes.resetPasswordToken.should.not.be.empty();
should.exist(userRes.resetPasswordExpires);
res.body.message.should.be.equal('Failure sending email');
@@ -421,7 +470,7 @@ describe('User CRUD tests', function () {
should.not.exist(err);
agent.post('/api/auth/forgot')
.send({
username: user.username
usernameOrEmail: user.username
})
.expect(400)
.end(function (err, res) {
@@ -430,7 +479,7 @@ describe('User CRUD tests', function () {
return done(err);
}
User.findOne({ username: user.username.toLowerCase() }, function(err, userRes) {
User.findOne({ username: user.username.toLowerCase() }, function (err, userRes) {
userRes.resetPasswordToken.should.not.be.empty();
should.exist(userRes.resetPasswordExpires);
@@ -458,7 +507,7 @@ describe('User CRUD tests', function () {
should.not.exist(err);
agent.post('/api/auth/forgot')
.send({
username: user.username
usernameOrEmail: user.username
})
.expect(400)
.end(function (err, res) {
@@ -1030,6 +1079,39 @@ describe('User CRUD tests', function () {
});
});
it('should be able to change profile picture and not fail if existing picture file does not exist', function (done) {
user.profileImageURL = config.uploads.profile.image.dest + 'non-existing.png';
user.save(function (saveErr) {
// Handle error
if (saveErr) {
return done(saveErr);
}
agent.post('/api/auth/signin')
.send(credentials)
.expect(200)
.end(function (signinErr) {
// Handle signin error
if (signinErr) {
return done(signinErr);
}
agent.post('/api/users/picture')
.attach('newProfilePicture', './modules/users/client/img/profile/default.png')
.expect(200)
.end(function (userInfoErr) {
should.not.exist(userInfoErr);
return done();
});
});
});
});
afterEach(function (done) {
User.remove().exec(done);
});

View File

@@ -32,35 +32,35 @@
"test:coverage": "gulp test:coverage",
"postinstall": "npm run bower",
"generate-ssl-certs": "scripts/generate-ssl-certs.sh",
"snyk-protect": "snyk protect",
"prepublish": "npm run snyk-protect"
"seed": "gulp seed",
"seed:prod": "gulp seed:prod",
"seed:test": "gulp seed:test"
},
"dependencies": {
"acl": "~0.4.10",
"async": "~2.3.0",
"async": "~2.5.0",
"bencoding": "0.0.1",
"body-parser": "~1.17.1",
"bower": "~1.8.0",
"chalk": "~2.0.1",
"chalk": "~2.1.0",
"compression": "~1.7.0",
"connect-flash": "~0.1.1",
"connect-mongo": "~1.3.2",
"cookie-parser": "~1.4.1",
"crypto": "0.0.3",
"express": "~4.15.2",
"express-hbs": "^1.0.4",
"express-session": "~1.15.2",
"generate-password": "~1.3.0",
"glob": "~7.1.1",
"helmet": "~3.6.1",
"helmet": "~3.8.1",
"irc": "^0.5.2",
"jasmine-core": "~2.5.2",
"jasmine-core": "~2.7.0",
"lodash": "~4.17.4",
"lusca": "~1.4.1",
"lusca": "~1.5.1",
"method-override": "~2.3.8",
"mocha": "~3.4.2",
"moment": "^2.18.1",
"mongoose": "~4.10.2",
"mongoose": "~4.11.3",
"morgan": "~1.8.1",
"moviedb": "^0.2.8",
"multer": "~1.3.0",
@@ -87,16 +87,15 @@
"devDependencies": {
"coveralls": "~2.13.0",
"del": "^3.0.0",
"eslint": "~2.2.0",
"eslint-config-airbnb": "~6.0.2",
"gulp": "~3.9.1",
"gulp-angular-templatecache": "~2.0.0",
"gulp-autoprefixer": "~3.1.0",
"gulp-autoprefixer": "~4.0.0",
"gulp-concat": "~2.6.0",
"gulp-csslint": "~1.0.0",
"gulp-csso": "~3.0.0",
"gulp-eslint": "~3.0.1",
"gulp-imagemin": "~3.2.0",
"gulp-imagemin": "~3.3.0",
"gulp-istanbul": "~1.1.1",
"gulp-less": "~3.3.0",
"gulp-load-plugins": "~1.5.0",
@@ -106,23 +105,19 @@
"gulp-protractor": "^4.0.0",
"gulp-refresh": "~1.1.0",
"gulp-rename": "~1.2.2",
"gulp-rev": "^7.1.2",
"gulp-rev": "~8.0.0",
"gulp-sass": "~3.1.0",
"gulp-uglify": "~2.1.2",
"gulp-util": "~3.0.7",
"gulp-uglify": "~3.0.0",
"imagemin-pngquant": "~5.0.0",
"istanbul": "~0.4.2",
"karma": "~1.6.0",
"karma-chrome-launcher": "~2.0.0",
"karma": "~1.7.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage": "~1.1.1",
"karma-jasmine": "~1.1.0",
"karma-mocha-reporter": "~2.2.3",
"karma-ng-html2js-preprocessor": "~1.0.0",
"lcov-result-merger": "~1.2.0",
"run-sequence": "~2.0.0",
"semver": "~5.3.0",
"run-sequence": "~2.1.0",
"semver": "~5.4.1",
"should": "~11.2.1",
"supertest": "~3.0.0"
},
"snyk": true
}
}