Skip to content
This repository was archived by the owner on Aug 30, 2021. It is now read-only.

Commit 69b8a05

Browse files
committed
fix(articles): Article edit/delete validation
Adds a custom field named `isCurrentUserOwner` to the Article document before it's returned to the client. This field is used to determine if the current User should is the "owner", and should see the edit/delete controls on the client-side when viewing a single article. This custom (ad-hoc) field is NOT persisted to the database; it's merely attached to the document. Added server-side route tests for verifying the ad-hoc "isCurrentUserOwner" field is properly set on the a single Article document. Fixes #1146
1 parent ce3d006 commit 69b8a05

File tree

3 files changed

+166
-2
lines changed

3 files changed

+166
-2
lines changed

modules/articles/client/views/view-article.client.view.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<div class="page-header">
33
<h1 ng-bind="vm.article.title"></h1>
44
</div>
5-
<div class="pull-right" ng-show="vm.authentication.user._id == vm.article.user._id">
5+
<div class="pull-right" ng-show="vm.article.isCurrentUserOwner">
66
<a class="btn btn-primary" ui-sref="articles.edit({ articleId: vm.article._id })">
77
<i class="glyphicon glyphicon-edit"></i>
88
</a>

modules/articles/server/controllers/articles.server.controller.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,14 @@ exports.create = function (req, res) {
3030
* Show the current article
3131
*/
3232
exports.read = function (req, res) {
33-
res.json(req.article);
33+
// convert mongoose document to JSON
34+
var article = req.article ? req.article.toJSON() : {};
35+
36+
// Add a custom field to the Article, for determining if the current User is the "owner".
37+
// NOTE: This field is NOT persisted to the database, since it doesn't exist in the Article model.
38+
article.isCurrentUserOwner = req.user && article.user && article.user._id.toString() === req.user._id.toString() ? true : false;
39+
40+
res.json(article);
3441
};
3542

3643
/**

modules/articles/tests/server/article.server.routes.tests.js

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,163 @@ describe('Article CRUD tests', function () {
400400
});
401401
});
402402

403+
it('should be able to get a single article if signed in and verify the custom "isCurrentUserOwner" field is set to "true"', function (done) {
404+
// Create new article model instance
405+
article.user = user;
406+
var articleObj = new Article(article);
407+
408+
// Save the article
409+
articleObj.save(function () {
410+
agent.post('/api/auth/signin')
411+
.send(credentials)
412+
.expect(200)
413+
.end(function (signinErr, signinRes) {
414+
// Handle signin error
415+
if (signinErr) {
416+
return done(signinErr);
417+
}
418+
419+
// Get the userId
420+
var userId = user.id;
421+
422+
// Save a new article
423+
agent.post('/api/articles')
424+
.send(article)
425+
.expect(200)
426+
.end(function (articleSaveErr, articleSaveRes) {
427+
// Handle article save error
428+
if (articleSaveErr) {
429+
return done(articleSaveErr);
430+
}
431+
432+
// Get the article
433+
agent.get('/api/articles/' + articleSaveRes.body._id)
434+
.expect(200)
435+
.end(function (articleInfoErr, articleInfoRes) {
436+
// Handle article error
437+
if (articleInfoErr) {
438+
return done(articleInfoErr);
439+
}
440+
441+
// Set assertions
442+
(articleInfoRes.body._id).should.equal(articleSaveRes.body._id);
443+
(articleInfoRes.body.title).should.equal(article.title);
444+
445+
// Assert that the "isCurrentUserOwner" field is set to true since the current User created it
446+
(articleInfoRes.body.isCurrentUserOwner).should.equal(true);
447+
448+
// Call the assertion callback
449+
done();
450+
});
451+
});
452+
});
453+
});
454+
});
455+
456+
it('should be able to get a single article if not signed in and verify the custom "isCurrentUserOwner" field is set to "false"', function (done) {
457+
// Create new article model instance
458+
var articleObj = new Article(article);
459+
460+
// Save the article
461+
articleObj.save(function () {
462+
request(app).get('/api/articles/' + articleObj._id)
463+
.end(function (req, res) {
464+
// Set assertion
465+
res.body.should.be.instanceof(Object).and.have.property('title', article.title);
466+
// Assert the custom field "isCurrentUserOwner" is set to false for the un-authenticated User
467+
res.body.should.be.instanceof(Object).and.have.property('isCurrentUserOwner', false);
468+
// Call the assertion callback
469+
done();
470+
});
471+
});
472+
});
473+
474+
it('should be able to get single article, that a different user created, if logged in & verify the "isCurrentUserOwner" field is set to "false"', function (done) {
475+
// Create temporary user creds
476+
var _creds = {
477+
username: 'temp',
478+
password: '[email protected]$Aw3$0m3'
479+
};
480+
481+
// Create temporary user
482+
var _user = new User({
483+
firstName: 'Full',
484+
lastName: 'Name',
485+
displayName: 'Full Name',
486+
487+
username: _creds.username,
488+
password: _creds.password,
489+
provider: 'local'
490+
});
491+
492+
_user.save(function (err, _user) {
493+
// Handle save error
494+
if (err) {
495+
return done(err);
496+
}
497+
498+
// Sign in with the user that will create the Article
499+
agent.post('/api/auth/signin')
500+
.send(credentials)
501+
.expect(200)
502+
.end(function (signinErr, signinRes) {
503+
// Handle signin error
504+
if (signinErr) {
505+
return done(signinErr);
506+
}
507+
508+
// Get the userId
509+
var userId = user._id;
510+
511+
// Save a new article
512+
agent.post('/api/articles')
513+
.send(article)
514+
.expect(200)
515+
.end(function (articleSaveErr, articleSaveRes) {
516+
// Handle article save error
517+
if (articleSaveErr) {
518+
return done(articleSaveErr);
519+
}
520+
521+
// Set assertions on new article
522+
(articleSaveRes.body.title).should.equal(article.title);
523+
should.exist(articleSaveRes.body.user);
524+
should.equal(articleSaveRes.body.user._id, userId);
525+
526+
// now signin with the temporary user
527+
agent.post('/api/auth/signin')
528+
.send(_creds)
529+
.expect(200)
530+
.end(function (err, res) {
531+
// Handle signin error
532+
if (err) {
533+
return done(err);
534+
}
535+
536+
// Get the article
537+
agent.get('/api/articles/' + articleSaveRes.body._id)
538+
.expect(200)
539+
.end(function (articleInfoErr, articleInfoRes) {
540+
// Handle article error
541+
if (articleInfoErr) {
542+
return done(articleInfoErr);
543+
}
544+
545+
// Set assertions
546+
(articleInfoRes.body._id).should.equal(articleSaveRes.body._id);
547+
(articleInfoRes.body.title).should.equal(article.title);
548+
// Assert that the custom field "isCurrentUserOwner" is set to false since the current User didn't create it
549+
(articleInfoRes.body.isCurrentUserOwner).should.equal(false);
550+
551+
// Call the assertion callback
552+
done();
553+
});
554+
});
555+
});
556+
});
557+
});
558+
});
559+
403560
afterEach(function (done) {
404561
User.remove().exec(function () {
405562
Article.remove().exec(done);

0 commit comments

Comments
 (0)