apostrophe 2.223.0 → 2.224.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/lib/modules/apostrophe-areas/lib/api.js +3 -4
- package/lib/modules/apostrophe-login/index.js +1 -1
- package/lib/modules/apostrophe-pages/index.js +10 -6
- package/lib/modules/apostrophe-pages/lib/api.js +67 -15
- package/lib/modules/apostrophe-users/index.js +38 -26
- package/package.json +2 -3
- package/test/pages.js +24 -3
- package/test/users.js +29 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.224.0 (2023-02-01)
|
|
4
|
+
|
|
5
|
+
### Adds
|
|
6
|
+
|
|
7
|
+
* If about to 404, redirect uppercase URLs automatically to their lowercase version - can be disabled with `redirectFailedUpperCaseUrls: false` in `apostrophe-pages/index.js` options.
|
|
8
|
+
|
|
9
|
+
## 2.223.1 (2022-12-21)
|
|
10
|
+
|
|
11
|
+
### Fixes
|
|
12
|
+
|
|
13
|
+
* Replace [`credential`](https://www.npmjs.com/package/credential) package with [`credentials`](https://www.npmjs.com/package/credentials) to fix the [`mout` Prototype Pollution vulnerability scanner warning](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-7792). There was no actual vulnerability in Apostrophe or credential due to the way the module was used.
|
|
14
|
+
* Fix vulnerability scanner warning by removing package `deep-get-set` used in areas. There was no actual vulnerability in Apostrophe due to the way the module was used.
|
|
15
|
+
|
|
3
16
|
## 2.223.0 (2022-11-28)
|
|
4
17
|
|
|
5
18
|
### Adds
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
var _ = require('@sailshq/lodash');
|
|
2
2
|
var async = require('async');
|
|
3
|
-
var deep = require('deep-get-set');
|
|
4
3
|
|
|
5
4
|
module.exports = function(self, options) {
|
|
6
5
|
|
|
@@ -213,13 +212,13 @@ module.exports = function(self, options) {
|
|
|
213
212
|
// and is not an area.
|
|
214
213
|
|
|
215
214
|
if (components.length > 1) {
|
|
216
|
-
var existing =
|
|
215
|
+
var existing = _.get(doc, dotPath);
|
|
217
216
|
if (existing && (existing.type !== 'area')) {
|
|
218
217
|
return callback(new Error('forbidden'));
|
|
219
218
|
}
|
|
220
219
|
}
|
|
221
220
|
|
|
222
|
-
var existingArea =
|
|
221
|
+
var existingArea = _.get(doc, dotPath);
|
|
223
222
|
var existingItems = (existingArea && existingArea.items) || [];
|
|
224
223
|
if (_.isEqual(self.apos.utils.clonePermanent(items), self.apos.utils.clonePermanent(existingItems))) {
|
|
225
224
|
// No real change — don't waste a version and clutter the database.
|
|
@@ -228,7 +227,7 @@ module.exports = function(self, options) {
|
|
|
228
227
|
return setImmediate(callback);
|
|
229
228
|
}
|
|
230
229
|
|
|
231
|
-
|
|
230
|
+
_.set(doc, dotPath, {
|
|
232
231
|
type: 'area',
|
|
233
232
|
items: items
|
|
234
233
|
});
|
|
@@ -289,7 +289,7 @@ module.exports = {
|
|
|
289
289
|
// Users with the `disabled` property set to true may not log in.
|
|
290
290
|
// Passwords are verified via the `verifyPassword` method of
|
|
291
291
|
// [apostrophe-users](/reference/modules/apostrophe-users), which is
|
|
292
|
-
// powered by the [
|
|
292
|
+
// powered by the [credentials](https://npmjs.org/package/credentials) module.
|
|
293
293
|
|
|
294
294
|
self.enableLocalStrategy = function() {
|
|
295
295
|
self.passport.use(new LocalStrategy(self.verifyLogin));
|
|
@@ -255,6 +255,8 @@ module.exports = {
|
|
|
255
255
|
|
|
256
256
|
alias: 'pages',
|
|
257
257
|
|
|
258
|
+
redirectFailedUpperCaseUrls: true,
|
|
259
|
+
|
|
258
260
|
types: [
|
|
259
261
|
{
|
|
260
262
|
// So that the minimum parked pages don't result in an error when home has no manager. -Tom
|
|
@@ -324,12 +326,14 @@ module.exports = {
|
|
|
324
326
|
name: 'trash',
|
|
325
327
|
label: 'Trash'
|
|
326
328
|
}
|
|
327
|
-
].concat(options.apos.docs.trashInSchema
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
329
|
+
].concat(options.apos.docs.trashInSchema
|
|
330
|
+
? [
|
|
331
|
+
{
|
|
332
|
+
name: 'rescue',
|
|
333
|
+
label: 'Rescue'
|
|
334
|
+
}
|
|
335
|
+
]
|
|
336
|
+
: []).concat([
|
|
333
337
|
{
|
|
334
338
|
name: 'publish',
|
|
335
339
|
label: 'Publish'
|
|
@@ -429,7 +429,13 @@ module.exports = function(self, options) {
|
|
|
429
429
|
.trash(null)
|
|
430
430
|
.published(null)
|
|
431
431
|
.areas(false)
|
|
432
|
-
.ancestors(_.assign({
|
|
432
|
+
.ancestors(_.assign({
|
|
433
|
+
depth: 1,
|
|
434
|
+
trash: null,
|
|
435
|
+
published: null,
|
|
436
|
+
areas: false,
|
|
437
|
+
permission: false
|
|
438
|
+
}, options.filters || {}))
|
|
433
439
|
.applyFilters(options.filters || {})
|
|
434
440
|
.toObject(function(err, page) {
|
|
435
441
|
if (err) {
|
|
@@ -460,7 +466,13 @@ module.exports = function(self, options) {
|
|
|
460
466
|
.trash(null)
|
|
461
467
|
.published(null)
|
|
462
468
|
.areas(false)
|
|
463
|
-
.ancestors(_.assign({
|
|
469
|
+
.ancestors(_.assign({
|
|
470
|
+
depth: 1,
|
|
471
|
+
trash: null,
|
|
472
|
+
published: null,
|
|
473
|
+
areas: false,
|
|
474
|
+
permission: false
|
|
475
|
+
}, options.filters || {}))
|
|
464
476
|
.applyFilters(options.filters || {})
|
|
465
477
|
.toObject(function(err, page) {
|
|
466
478
|
if (err) {
|
|
@@ -516,7 +528,11 @@ module.exports = function(self, options) {
|
|
|
516
528
|
function nudgeNewPeers(callback) {
|
|
517
529
|
// Nudge down the pages that should now follow us
|
|
518
530
|
// Always remember multi: true
|
|
519
|
-
self.apos.docs.db.update(mergeCriteria({
|
|
531
|
+
self.apos.docs.db.update(mergeCriteria({
|
|
532
|
+
path: self.matchDescendants(parent),
|
|
533
|
+
level: parent.level + 1,
|
|
534
|
+
rank: { $gte: rank }
|
|
535
|
+
}), { $inc: { rank: 1 } }, { multi: true }, function(err, count) {
|
|
520
536
|
return callback(err);
|
|
521
537
|
});
|
|
522
538
|
}
|
|
@@ -738,7 +754,12 @@ module.exports = function(self, options) {
|
|
|
738
754
|
[
|
|
739
755
|
function getPage(cb) {
|
|
740
756
|
// check permissions and load page to trash/untrash
|
|
741
|
-
return self.find(req, { _id: _id }).permission('edit').trash(null).ancestors({
|
|
757
|
+
return self.find(req, { _id: _id }).permission('edit').trash(null).ancestors({
|
|
758
|
+
depth: 1,
|
|
759
|
+
published: null,
|
|
760
|
+
trash: null,
|
|
761
|
+
areas: false
|
|
762
|
+
}).toObject((err, _page) => {
|
|
742
763
|
page = _page;
|
|
743
764
|
tree.push(page);
|
|
744
765
|
parent = page._ancestors[0];
|
|
@@ -876,14 +897,17 @@ module.exports = function(self, options) {
|
|
|
876
897
|
var parent;
|
|
877
898
|
var changed = [];
|
|
878
899
|
|
|
879
|
-
return async.series([findTrash, findPage, movePage], function(err) {
|
|
900
|
+
return async.series([ findTrash, findPage, movePage ], function(err) {
|
|
880
901
|
return callback(err, parent && parent.slug, changed);
|
|
881
902
|
});
|
|
882
903
|
|
|
883
904
|
function findTrash(callback) {
|
|
884
905
|
// Always only one trash page at level 1, so we don't have
|
|
885
906
|
// to hardcode the slug
|
|
886
|
-
return self.find(req, {
|
|
907
|
+
return self.find(req, {
|
|
908
|
+
trash: true,
|
|
909
|
+
level: 1
|
|
910
|
+
})
|
|
887
911
|
.permission(false)
|
|
888
912
|
.published(null)
|
|
889
913
|
.trash(null)
|
|
@@ -899,7 +923,12 @@ module.exports = function(self, options) {
|
|
|
899
923
|
|
|
900
924
|
function findPage(callback) {
|
|
901
925
|
// Also checks permissions
|
|
902
|
-
return self.find(req, { _id: _id }).permission('edit').ancestors({
|
|
926
|
+
return self.find(req, { _id: _id }).permission('edit').ancestors({
|
|
927
|
+
depth: 1,
|
|
928
|
+
published: null,
|
|
929
|
+
trash: null,
|
|
930
|
+
areas: false
|
|
931
|
+
}).toObject(function(err, _page) {
|
|
903
932
|
if (err || (!_page)) {
|
|
904
933
|
return callback('Page not found');
|
|
905
934
|
}
|
|
@@ -943,7 +972,12 @@ module.exports = function(self, options) {
|
|
|
943
972
|
|
|
944
973
|
function findPage(callback) {
|
|
945
974
|
// Also checks permissions
|
|
946
|
-
return self.find(req, { _id: _id }).permission('publish').trash(true).ancestors({
|
|
975
|
+
return self.find(req, { _id: _id }).permission('publish').trash(true).ancestors({
|
|
976
|
+
depth: 1,
|
|
977
|
+
published: null,
|
|
978
|
+
trash: null,
|
|
979
|
+
areas: false
|
|
980
|
+
}).toObject(function(err, _page) {
|
|
947
981
|
if (err || (!_page)) {
|
|
948
982
|
return callback('Page not found');
|
|
949
983
|
}
|
|
@@ -1134,6 +1168,12 @@ module.exports = function(self, options) {
|
|
|
1134
1168
|
if (self.isFound(req)) {
|
|
1135
1169
|
return callback(null);
|
|
1136
1170
|
}
|
|
1171
|
+
|
|
1172
|
+
if (options.redirectFailedUpperCaseUrls && /[A-Z]/.test(req.path)) {
|
|
1173
|
+
req.redirect = self.apos.urls.build(req.path.toLowerCase(), req.query);
|
|
1174
|
+
return callback(null);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1137
1177
|
req.data.suggestedSearch = self.apos.utils.slugify(req.url, { separator: ' ' });
|
|
1138
1178
|
req.notFound = true;
|
|
1139
1179
|
req.res.statusCode = 404;
|
|
@@ -1141,7 +1181,7 @@ module.exports = function(self, options) {
|
|
|
1141
1181
|
// Give the browser a chance to do something interesting with a 404.
|
|
1142
1182
|
// This is often a better idea than doing a heavy fallback search
|
|
1143
1183
|
// server side, because those are triggered heavily by bots
|
|
1144
|
-
req.browserCall(
|
|
1184
|
+
req.browserCall('apos.emit(\'notfound\', ?)', {
|
|
1145
1185
|
suggestedSearch: req.data.suggestedSearch,
|
|
1146
1186
|
url: req.url
|
|
1147
1187
|
});
|
|
@@ -1421,7 +1461,10 @@ module.exports = function(self, options) {
|
|
|
1421
1461
|
|
|
1422
1462
|
self.ensurePathIndex = function(callback) {
|
|
1423
1463
|
var params = self.getPathIndexParams();
|
|
1424
|
-
return self.apos.docs.db.ensureIndex(params, {
|
|
1464
|
+
return self.apos.docs.db.ensureIndex(params, {
|
|
1465
|
+
unique: true,
|
|
1466
|
+
sparse: true
|
|
1467
|
+
}, callback);
|
|
1425
1468
|
};
|
|
1426
1469
|
|
|
1427
1470
|
self.getPathIndexParams = function() {
|
|
@@ -1502,8 +1545,14 @@ module.exports = function(self, options) {
|
|
|
1502
1545
|
var matchParentPathPrefix = new RegExp('^' + self.apos.utils.regExpQuote(originalPath + '/'));
|
|
1503
1546
|
var matchParentSlugPrefix = new RegExp('^' + self.apos.utils.regExpQuote(originalSlug + '/'));
|
|
1504
1547
|
var done = false;
|
|
1505
|
-
var cursor = self.apos.docs.db.findWithProjection(mergeCriteria({ path: matchParentPathPrefix }), {
|
|
1506
|
-
|
|
1548
|
+
var cursor = self.apos.docs.db.findWithProjection(mergeCriteria({ path: matchParentPathPrefix }), {
|
|
1549
|
+
slug: 1,
|
|
1550
|
+
path: 1,
|
|
1551
|
+
level: 1
|
|
1552
|
+
});
|
|
1553
|
+
return async.whilst(function() {
|
|
1554
|
+
return !done;
|
|
1555
|
+
}, function(callback) {
|
|
1507
1556
|
return cursor.nextObject(function(err, desc) {
|
|
1508
1557
|
if (err) {
|
|
1509
1558
|
return callback(err);
|
|
@@ -1996,10 +2045,10 @@ module.exports = function(self, options) {
|
|
|
1996
2045
|
self.validateTypeChoices = function() {
|
|
1997
2046
|
_.each(self.typeChoices, function(choice) {
|
|
1998
2047
|
if (!choice.name) {
|
|
1999
|
-
throw new Error(
|
|
2048
|
+
throw new Error('One of the page types specified for your \'types\' option has no \'name\' property.');
|
|
2000
2049
|
}
|
|
2001
2050
|
if (!choice.label) {
|
|
2002
|
-
throw new Error(
|
|
2051
|
+
throw new Error('One of the page types specified for your \'types\' option has no \'label\' property.');
|
|
2003
2052
|
}
|
|
2004
2053
|
});
|
|
2005
2054
|
};
|
|
@@ -2060,7 +2109,10 @@ module.exports = function(self, options) {
|
|
|
2060
2109
|
|
|
2061
2110
|
self.removeSlugFromHomepageSchema = function(page, schema) {
|
|
2062
2111
|
if (page.level === 0) {
|
|
2063
|
-
schema = _.reject(schema, {
|
|
2112
|
+
schema = _.reject(schema, {
|
|
2113
|
+
type: 'slug',
|
|
2114
|
+
name: 'slug'
|
|
2115
|
+
});
|
|
2064
2116
|
}
|
|
2065
2117
|
return schema;
|
|
2066
2118
|
};
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
//
|
|
38
38
|
// For security the `password` property is not stored as plaintext and
|
|
39
39
|
// is not kept in the aposDocs collection. Instead, it is hashed and salted
|
|
40
|
-
// using the `
|
|
40
|
+
// using the `credentials` module and the resulting hash is stored
|
|
41
41
|
// in a separate `aposUsersSafe` collection.
|
|
42
42
|
//
|
|
43
43
|
// Additional secrets may be hashed in this way. If you set the
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
|
|
63
63
|
var async = require('async');
|
|
64
64
|
var _ = require('@sailshq/lodash');
|
|
65
|
-
var
|
|
65
|
+
var credentials = require('credentials');
|
|
66
66
|
var Promise = require('bluebird');
|
|
67
67
|
|
|
68
68
|
module.exports = {
|
|
@@ -82,7 +82,6 @@ module.exports = {
|
|
|
82
82
|
slugPrefix: 'user-',
|
|
83
83
|
|
|
84
84
|
afterConstruct: function(self, callback) {
|
|
85
|
-
self.initializeCredential();
|
|
86
85
|
self.addOurTrashPrefixFields();
|
|
87
86
|
self.enableSecrets();
|
|
88
87
|
self.addNonNullJoinMigration();
|
|
@@ -447,14 +446,17 @@ module.exports = {
|
|
|
447
446
|
if (!doc[secret]) {
|
|
448
447
|
return callback(null);
|
|
449
448
|
}
|
|
450
|
-
return
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
449
|
+
return credentials
|
|
450
|
+
.hash(doc[secret])
|
|
451
|
+
.then(hash => {
|
|
452
|
+
const annotatedHash = JSON.stringify(
|
|
453
|
+
Object.assign(JSON.parse(hash), { credentials3: true })
|
|
454
|
+
);
|
|
455
|
+
delete doc[secret];
|
|
456
|
+
safeUser[secret + 'Hash'] = annotatedHash;
|
|
457
|
+
callback(null);
|
|
458
|
+
})
|
|
459
|
+
.catch(callback);
|
|
458
460
|
};
|
|
459
461
|
|
|
460
462
|
// Verify the given password by checking it against the
|
|
@@ -473,7 +475,7 @@ module.exports = {
|
|
|
473
475
|
// in `options.secrets` when configuring this module or via
|
|
474
476
|
// `addSecrets` are not stored as plaintext and are not kept in the
|
|
475
477
|
// aposDocs collection. Instead, they are hashed and salted using the
|
|
476
|
-
// `
|
|
478
|
+
// `credentials` module and the resulting hash is stored
|
|
477
479
|
// in a separate `aposUsersSafe` collection. This method
|
|
478
480
|
// can be used to verify that `attempt` matches the
|
|
479
481
|
// previously hashed value for the property named `secret`,
|
|
@@ -504,18 +506,33 @@ module.exports = {
|
|
|
504
506
|
if (!safeUser) {
|
|
505
507
|
return callback(new Error('No such user in the safe.'));
|
|
506
508
|
}
|
|
507
|
-
return
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
509
|
+
return credentials
|
|
510
|
+
.verify(migrate(safeUser[secret + 'Hash']), attempt)
|
|
511
|
+
.then(isValid => {
|
|
512
|
+
if (!isValid) {
|
|
513
|
+
throw new Error('Incorrect ' + secret);
|
|
514
|
+
}
|
|
515
|
+
callback(null);
|
|
516
|
+
})
|
|
517
|
+
.catch(callback);
|
|
516
518
|
}
|
|
517
519
|
}, callback);
|
|
518
520
|
}
|
|
521
|
+
function migrate(json) {
|
|
522
|
+
const data = JSON.parse(json);
|
|
523
|
+
|
|
524
|
+
// Do not re-encode salt generated by credentials@3
|
|
525
|
+
if (data.credentials3) {
|
|
526
|
+
return json;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return JSON.stringify(
|
|
530
|
+
Object.assign(
|
|
531
|
+
data,
|
|
532
|
+
{ salt: Buffer.from(data.salt, 'utf8').toString('base64') }
|
|
533
|
+
)
|
|
534
|
+
);
|
|
535
|
+
}
|
|
519
536
|
};
|
|
520
537
|
|
|
521
538
|
// Forget the secret associated with the property name
|
|
@@ -651,11 +668,6 @@ module.exports = {
|
|
|
651
668
|
});
|
|
652
669
|
};
|
|
653
670
|
|
|
654
|
-
// Initialize the [credential](https://npmjs.org/package/credential) module.
|
|
655
|
-
self.initializeCredential = function() {
|
|
656
|
-
self.pw = credential();
|
|
657
|
-
};
|
|
658
|
-
|
|
659
671
|
self.apos.tasks.add('apostrophe-users', 'add',
|
|
660
672
|
'Usage: node app apostrophe-users:add username groupname\n\n' +
|
|
661
673
|
'This adds a new user and assigns them to a group.\n' +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apostrophe",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.224.0",
|
|
4
4
|
"description": "The Apostrophe Content Management System.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -36,9 +36,8 @@
|
|
|
36
36
|
"connect-flash": "^0.1.1",
|
|
37
37
|
"connect-multiparty": "^2.2.0",
|
|
38
38
|
"cookie-parser": "^1.4.5",
|
|
39
|
-
"
|
|
39
|
+
"credentials": "^3.0.2",
|
|
40
40
|
"cuid": "^1.3.8",
|
|
41
|
-
"deep-get-set": "^1.1.1",
|
|
42
41
|
"diff": "^4.0.1",
|
|
43
42
|
"emulate-mongo-2-driver": "^1.2.3",
|
|
44
43
|
"express": "^4.17.1",
|
package/test/pages.js
CHANGED
|
@@ -433,7 +433,6 @@ describe('Pages', function() {
|
|
|
433
433
|
assert(body.match(/Home: \//));
|
|
434
434
|
// Does the response prove that data.home._children was available?
|
|
435
435
|
assert(body.match(/Tab: \/another-parent/));
|
|
436
|
-
// console.log(body);
|
|
437
436
|
return done();
|
|
438
437
|
});
|
|
439
438
|
});
|
|
@@ -447,7 +446,6 @@ describe('Pages', function() {
|
|
|
447
446
|
assert(body.match(/Home: \//));
|
|
448
447
|
// Does the response prove that data.home._children was available?
|
|
449
448
|
assert(body.match(/Tab: \/another-parent/));
|
|
450
|
-
// console.log(body);
|
|
451
449
|
return done();
|
|
452
450
|
});
|
|
453
451
|
});
|
|
@@ -475,6 +473,29 @@ describe('Pages', function() {
|
|
|
475
473
|
});
|
|
476
474
|
});
|
|
477
475
|
|
|
476
|
+
it('should redirect to url with path in lower case by default', function(done) {
|
|
477
|
+
return request('http://localhost:7900/chiLD', function (err, response, body) {
|
|
478
|
+
assert(!err);
|
|
479
|
+
assert.equal(response.statusCode, 200);
|
|
480
|
+
assert(body.match(/Sing to me, Oh Muse./));
|
|
481
|
+
assert(body.match(/Home: \//));
|
|
482
|
+
assert(body.match(/Tab: \/another-parent/));
|
|
483
|
+
return done();
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('should not redirect to url with path in lower case if the option is disabled', function(done) {
|
|
488
|
+
apos.pages.options.redirectFailedUpperCaseUrls = false;
|
|
489
|
+
|
|
490
|
+
return request('http://localhost:7900/chiLD', function (err, response, body) {
|
|
491
|
+
assert(!err);
|
|
492
|
+
assert.equal(response.statusCode, 404);
|
|
493
|
+
apos.pages.options.redirectFailedUpperCaseUrls = true;
|
|
494
|
+
return done();
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
});
|
|
498
|
+
|
|
478
499
|
it('should detect that the home page is an ancestor of any page except itself', function() {
|
|
479
500
|
assert(
|
|
480
501
|
apos.pages.isAncestorOf({
|
|
@@ -561,8 +582,8 @@ describe('Pages', function() {
|
|
|
561
582
|
assert.equal(page.path, '/newish-page');
|
|
562
583
|
done();
|
|
563
584
|
});
|
|
564
|
-
});
|
|
565
585
|
|
|
586
|
+
});
|
|
566
587
|
});
|
|
567
588
|
|
|
568
589
|
describe('Pages with trashInSchema', function() {
|
package/test/users.js
CHANGED
|
@@ -103,6 +103,35 @@ describe('Users', function() {
|
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
+
it('should verify a user password created with former credential package', function(done) {
|
|
107
|
+
var req = apos.tasks.getReq();
|
|
108
|
+
var user = apos.users.newInstance();
|
|
109
|
+
|
|
110
|
+
user.firstName = 'Old';
|
|
111
|
+
user.lastName = 'User';
|
|
112
|
+
user.title = 'Old User';
|
|
113
|
+
user.username = 'olduser';
|
|
114
|
+
user.password = 'passwordThatThroughOldCredentialPackageHashing';
|
|
115
|
+
user.email = 'old@user.com';
|
|
116
|
+
|
|
117
|
+
apos.users.insert(req, user, async function(err) {
|
|
118
|
+
assert(!err);
|
|
119
|
+
|
|
120
|
+
// A password hash that were generated by the former credential package:
|
|
121
|
+
var oldPasswordHashSimulated =
|
|
122
|
+
'{"hash":"HKBAyPWKKnKnXzF0yflRUEeeJZk1njKaX3IqT6Ml056OdMWIsDRqfJeHCqxI3jA9HEFNzuPEhw0m98dA8ju8xRpj","salt":"P2X4+Ex0rrHSPBRv0TCGOTqXmuT2JDspNLc/0Uln6jcZWACUpgBz+DDpfP9DFZcPG9cMlwMaHEKw3MVq02af8RSn","keyLength":66,"hashMethod":"pbkdf2","iterations":2853010}';
|
|
123
|
+
|
|
124
|
+
apos.users.safe.update({ username: 'olduser' }, { $set: { passwordHash: oldPasswordHashSimulated } }, function() {
|
|
125
|
+
apos.users.verifyPassword(user, 'passwordThatThroughOldCredentialPackageHashing', function(err) {
|
|
126
|
+
assert(!err);
|
|
127
|
+
apos.users.safe.remove({ username: 'olduser' }, function() {
|
|
128
|
+
done();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
106
135
|
it('should not verify an incorrect user password', function(done) {
|
|
107
136
|
apos.users.find(apos.tasks.getReq(), { username: 'JaneD' })
|
|
108
137
|
.toObject(function(err, user) {
|