diff --git a/client/coral-framework/helpers/validate.js b/client/coral-framework/helpers/validate.js index 6e5db3662..b267fb7c0 100644 --- a/client/coral-framework/helpers/validate.js +++ b/client/coral-framework/helpers/validate.js @@ -1,5 +1,5 @@ export default { - email: email => (/^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(email)), + email: email => (/^([A-Za-z0-9_\-\.\+])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(email)), password: pass => (/^(?=.{8,}).*$/.test(pass)), confirmPassword: () => true, displayName: displayName => (/^[a-zA-Z0-9_]+$/.test(displayName)) diff --git a/graph/loaders/comments.js b/graph/loaders/comments.js index 35463dfd0..9a67bb621 100644 --- a/graph/loaders/comments.js +++ b/graph/loaders/comments.js @@ -7,31 +7,41 @@ const CommentModel = require('../../models/comment'); const CommentsService = require('../../services/comments'); /** - * Retrieves comments by an array of asset id's. + * Retrieves comments by an array of asset id's, results are returned in reverse + * chronological order. * @param {Array} ids array of ids to lookup */ -const genCommentsByAssetID = (context, ids) => CommentModel.find({ - asset_id: { - $in: ids - }, - parent_id: null, - status: { - $in: [null, 'ACCEPTED'] - } -}).then(util.arrayJoinBy(ids, 'asset_id')); +const genCommentsByAssetID = (context, ids) => { + return CommentModel.find({ + asset_id: { + $in: ids + }, + parent_id: null, + status: { + $in: [null, 'ACCEPTED'] + } + }) + .sort({created_at: -1}) + .then(util.arrayJoinBy(ids, 'asset_id')); +}; /** - * Retrieves comments by an array of parent ids. + * Retrieves comments by an array of parent ids, results are returned in + * chronological order. * @param {Array} ids array of ids to lookup */ -const genCommentsByParentID = (context, ids) => CommentModel.find({ - parent_id: { - $in: ids - }, - status: { - $in: [null, 'ACCEPTED'] - } -}).then(util.arrayJoinBy(ids, 'parent_id')); +const genCommentsByParentID = (context, ids) => { + return CommentModel.find({ + parent_id: { + $in: ids + }, + status: { + $in: [null, 'ACCEPTED'] + } + }) + .sort({created_at: 1}) + .then(util.arrayJoinBy(ids, 'parent_id')); +}; const getCommentsByStatusAndAssetID = (context, {status = null, asset_id = null}) => { @@ -52,7 +62,7 @@ const getCommentsByActionTypeAndAssetID = (context, {action_type, asset_id = nul id: { $in: actions.map((action) => action.item_id) } - }); + }).sort({created_at: 1}); if (asset_id) { comments = comments.where({asset_id}); @@ -62,11 +72,15 @@ const getCommentsByActionTypeAndAssetID = (context, {action_type, asset_id = nul }); }; -const genCommentsByAuthorID = (context, authorIDs) => CommentModel.find({ - author_id: { - $in: authorIDs - } -}).then(util.arrayJoinBy(authorIDs, 'author_id')); +const genCommentsByAuthorID = (context, authorIDs) => { + return CommentModel.find({ + author_id: { + $in: authorIDs + } + }) + .sort({created_at: -1}) + .then(util.arrayJoinBy(authorIDs, 'author_id')); +}; /** * Creates a set of loaders based on a GraphQL context. diff --git a/graph/mutators/action.js b/graph/mutators/action.js index cf80c496d..ab51c9a20 100644 --- a/graph/mutators/action.js +++ b/graph/mutators/action.js @@ -37,7 +37,7 @@ module.exports = (context) => { // TODO: refactor to something that'll return an error in the event an attempt // is made to mutate state while not logged in. There's got to be a better way // to do this. - if (context.user) { + if (context.user && context.user.can('mutation:createAction', 'mutation:deleteAction')) { return { Action: { create: (action) => createAction(context, action), diff --git a/graph/mutators/comment.js b/graph/mutators/comment.js index 6bd7cea26..cc62e48df 100644 --- a/graph/mutators/comment.js +++ b/graph/mutators/comment.js @@ -144,7 +144,7 @@ module.exports = (context) => { // TODO: refactor to something that'll return an error in the event an attempt // is made to mutate state while not logged in. There's got to be a better way // to do this. - if (context.user) { + if (context.user && context.user.can('mutation:createComment')) { return { Comment: { create: (comment) => createPublicComment(context, comment) diff --git a/graph/mutators/user.js b/graph/mutators/user.js index 22837caae..96049bbab 100644 --- a/graph/mutators/user.js +++ b/graph/mutators/user.js @@ -15,7 +15,7 @@ module.exports = (context) => { // TODO: refactor to something that'll return an error in the event an attempt // is made to mutate state while not logged in. There's got to be a better way // to do this. - if (context.user) { + if (context.user && context.user.can('mutation:updateUserSettings')) { return { User: { updateSettings: (settings) => updateUserSettings(context, settings) diff --git a/models/user.js b/models/user.js index 0b8edf0fd..9c447fa5d 100644 --- a/models/user.js +++ b/models/user.js @@ -130,6 +130,33 @@ UserSchema.method('hasRoles', function(...roles) { }); }); +/** + * All the graph operations that are available for a user. + * @type {Array} + */ +const USER_GRAPH_OPERATIONS = [ + 'mutation:createComment', + 'mutation:createAction', + 'mutation:deleteAction', + 'mutation:updateUserSettings' +]; + +/** + * Can returns true if the user is allowed to perform a specific graph + * operation. + */ +UserSchema.method('can', function(...actions) { + if (actions.some((action) => USER_GRAPH_OPERATIONS.indexOf(action) === -1)) { + throw new Error(`invalid actions: ${actions}`); + } + + if (this.status === 'BANNED') { + return false; + } + + return true; +}); + // Create the User model. const UserModel = mongoose.model('User', UserSchema);