diff --git a/README.md b/README.md index 43af11d..5d9d646 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,7 @@ var users = [ {name:"Admin User",email:"admin@example.com",roles:['admin']} ]; -_.each(users, function (user) { +users.forEach(function (user) { var id; id = Accounts.createUser({ @@ -218,7 +218,7 @@ _.each(users, function (user) { }); if (user.roles.length > 0) { - _.each(user.roles, function (role) { + user.roles.forEach(function (role) { Roles.createRole(role, {unlessExists: true}); }); // Need _id of existing user record so this call must come after `Accounts.createUser`. diff --git a/examples/flow-router-advanced/main/client/layouts.js b/examples/flow-router-advanced/main/client/layouts.js index 6e66fc6..f77746b 100644 --- a/examples/flow-router-advanced/main/client/layouts.js +++ b/examples/flow-router-advanced/main/client/layouts.js @@ -1,5 +1,5 @@ Template.mainLayout.helpers({ - notVerified: function () { + notVerified: function () { var user = Meteor.user() return !emailVerified(user) @@ -7,7 +7,7 @@ Template.mainLayout.helpers({ }) function emailVerified (user) { - return _.some(user.emails, function (email) { + return user.emails.some(function (email) { return email.verified }) } diff --git a/examples/flow-router-advanced/main/client/lib/app.js b/examples/flow-router-advanced/main/client/lib/app.js index d528259..1c5b790 100644 --- a/examples/flow-router-advanced/main/client/lib/app.js +++ b/examples/flow-router-advanced/main/client/lib/app.js @@ -3,7 +3,7 @@ App = {} "use strict" -_.extend(App, { +Object.assign(App, { // Separate routing package details from general app code. navigateTo: function (path) { diff --git a/examples/flow-router-advanced/main/server/startup/fixtures.js b/examples/flow-router-advanced/main/server/startup/fixtures.js index 0c93187..5f78d21 100644 --- a/examples/flow-router-advanced/main/server/startup/fixtures.js +++ b/examples/flow-router-advanced/main/server/startup/fixtures.js @@ -26,9 +26,9 @@ function createUsers () { {name:"Admin User",email:"admin@example.com",roles:['admin']} ]; - _.each(users, function (userData) { + users.forEach(function (userData) { var id - + console.log(userData); id = Accounts.createUser({ @@ -41,12 +41,12 @@ function createUsers () { Meteor.users.update({_id: id}, {$set:{'emails.0.verified': true}}); - _.each(userData.roles, function (role) { + userData.roles.forEach(function (role) { Roles.createRole(role, {unlessExists: true}); }); Roles.addUsersToRoles(id, userData.roles); - + }); } } diff --git a/examples/flow-router-advanced/secrets/server/fixtures.js b/examples/flow-router-advanced/secrets/server/fixtures.js index a12700b..47f163b 100644 --- a/examples/flow-router-advanced/secrets/server/fixtures.js +++ b/examples/flow-router-advanced/secrets/server/fixtures.js @@ -14,7 +14,7 @@ Meteor.startup(function () { function createSecrets () { var secrets - + if (Meteor.secrets.find().fetch().length === 0) { console.log('Creating secrets: '); @@ -23,7 +23,7 @@ function createSecrets () { {secret:"domain registration pw: apple3"} ] - _.each(secrets, function (secret) { + secrets.forEach(function (secret) { console.log(secret) Meteor.secrets.insert(secret); diff --git a/examples/flow-router/client/layouts.js b/examples/flow-router/client/layouts.js index 6e66fc6..f77746b 100644 --- a/examples/flow-router/client/layouts.js +++ b/examples/flow-router/client/layouts.js @@ -1,5 +1,5 @@ Template.mainLayout.helpers({ - notVerified: function () { + notVerified: function () { var user = Meteor.user() return !emailVerified(user) @@ -7,7 +7,7 @@ Template.mainLayout.helpers({ }) function emailVerified (user) { - return _.some(user.emails, function (email) { + return user.emails.some(function (email) { return email.verified }) } diff --git a/examples/flow-router/server/server.js b/examples/flow-router/server/server.js index ae6a845..dd853ac 100644 --- a/examples/flow-router/server/server.js +++ b/examples/flow-router/server/server.js @@ -12,7 +12,7 @@ Meteor.startup(function () { //////////////////////////////////////////////////////////////////// // Create Test Secrets // - + if (Meteor.secrets.find().fetch().length === 0) { Meteor.secrets.insert({secret:"ec2 password: apple2"}); Meteor.secrets.insert({secret:"domain registration pw: apple3"}); @@ -34,10 +34,10 @@ Meteor.startup(function () { {name:"Admin User",email:"admin@example.com",roles:['admin']} ]; - _.each(users, function (userData) { + users.forEach(function (userData) { var id, user; - + console.log(userData); id = Accounts.createUser({ @@ -49,12 +49,12 @@ Meteor.startup(function () { // email verification Meteor.users.update({_id: id}, {$set:{'emails.0.verified': true}}); - _.each(userData.roles, function (role) { + userData.roles.forEach(function (role) { Roles.createRole(role, {unlessExists: true}); }); Roles.addUsersToRoles(id, userData.roles); - + }); } @@ -102,7 +102,7 @@ Meteor.publish("users", function () { if (Roles.userIsInRole(user, ["admin","manage-users"])) { console.log('publishing users', this.userId); return Meteor.users.find({}, {fields: {emails: 1, profile: 1, roles: 1}}); - } + } this.stop(); return; diff --git a/examples/iron-router/client/routing.js b/examples/iron-router/client/routing.js index 71beb41..8b9b72b 100644 --- a/examples/iron-router/client/routing.js +++ b/examples/iron-router/client/routing.js @@ -11,7 +11,7 @@ Meteor.navigateTo = function (path) { } function emailVerified (user) { - return _.some(user.emails, function (email) { + return user.emails.some(function (email) { return email.verified }) } @@ -19,7 +19,7 @@ function emailVerified (user) { var filters = { /** - * ensure user is logged in and + * ensure user is logged in and * email verified */ authenticate: function () { diff --git a/examples/iron-router/server/server.js b/examples/iron-router/server/server.js index ae6a845..8df5daa 100644 --- a/examples/iron-router/server/server.js +++ b/examples/iron-router/server/server.js @@ -12,7 +12,7 @@ Meteor.startup(function () { //////////////////////////////////////////////////////////////////// // Create Test Secrets // - + if (Meteor.secrets.find().fetch().length === 0) { Meteor.secrets.insert({secret:"ec2 password: apple2"}); Meteor.secrets.insert({secret:"domain registration pw: apple3"}); @@ -34,10 +34,10 @@ Meteor.startup(function () { {name:"Admin User",email:"admin@example.com",roles:['admin']} ]; - _.each(users, function (userData) { + users.forEach(function (userData) { var id, user; - + console.log(userData); id = Accounts.createUser({ @@ -49,12 +49,12 @@ Meteor.startup(function () { // email verification Meteor.users.update({_id: id}, {$set:{'emails.0.verified': true}}); - _.each(userData.roles, function (role) { + userData.roles.forEach(function (role) { Roles.createRole(role, {unlessExists: true}); }); Roles.addUsersToRoles(id, userData.roles); - + }); } @@ -102,7 +102,7 @@ Meteor.publish("users", function () { if (Roles.userIsInRole(user, ["admin","manage-users"])) { console.log('publishing users', this.userId); return Meteor.users.find({}, {fields: {emails: 1, profile: 1, roles: 1}}); - } + } this.stop(); return; diff --git a/package.js b/package.js index d34bdc6..5379777 100644 --- a/package.js +++ b/package.js @@ -8,10 +8,9 @@ Package.describe({ Package.onUse(function (api) { var both = ['client', 'server']; - api.versionsFrom("METEOR@1.4.1"); + api.versionsFrom("METEOR@1.6"); - api.use(['underscore', - 'accounts-base', + api.use(['accounts-base', 'tracker', 'mongo', 'check'], both); @@ -28,7 +27,7 @@ Package.onUse(function (api) { }); Package.onTest(function (api) { - api.versionsFrom("METEOR@1.4.1"); + api.versionsFrom("METEOR@1.6"); var both = ['client', 'server']; @@ -36,7 +35,7 @@ Package.onTest(function (api) { api.use(['alanning:roles', 'accounts-password', - 'underscore', + 'mongo', 'tinytest'], both); api.addFiles('roles/tests/client.js', 'client'); diff --git a/roles/client/uiHelpers.js b/roles/client/uiHelpers.js index 523f98a..d9084b0 100644 --- a/roles/client/uiHelpers.js +++ b/roles/client/uiHelpers.js @@ -16,7 +16,7 @@ // // Use a semi-private variable rather than declaring UI // helpers directly so that we can unit test the helpers. -// XXX For some reason, the UI helpers are not registered +// XXX For some reason, the UI helpers are not registered // before the tests run. // Roles._uiHelpers = { @@ -40,7 +40,7 @@ Roles._uiHelpers = { * @param {String} [scope] Optional, name of scope to check. * @return {Boolean} `true` if current user is in at least one of the target roles. * @static - * @for UIHelpers + * @for UIHelpers */ isInRole: function (role, scope) { var user = Meteor.user(), @@ -51,7 +51,7 @@ Roles._uiHelpers = { if (!Match.test(role, String)) return false if (comma !== -1) { - roles = _.reduce(role.split(','), function (memo, r) { + roles = role.split(',').reduce(function (memo, r) { if (!r || !Roles._trim(r)) { return memo } @@ -83,10 +83,10 @@ if (Roles.debug && console.log) { if ('undefined' !== typeof Package.blaze && 'undefined' !== typeof Package.blaze.Blaze && 'function' === typeof Package.blaze.Blaze.registerHelper) { - _.each(Roles._uiHelpers, function (func, name) { + Object.entries(Roles._uiHelpers).forEach(([name, func]) => { if (Roles.debug && console.log) { console.log("[roles] registering Blaze helper '" + name + "'") } - Package.blaze.Blaze.registerHelper(name, func) + Package.blaze.Blaze.registerHelper(name, func) }) } diff --git a/roles/roles_common.js b/roles/roles_common.js index f1814ec..a3e7400 100644 --- a/roles/roles_common.js +++ b/roles/roles_common.js @@ -37,7 +37,7 @@ if ('undefined' === typeof Roles) { var getGroupsForUserDeprecationWarning = false; -_.extend(Roles, { +Object.assign(Roles, { /** * Used as a global group (now scope) name. Not used anymore. @@ -65,9 +65,9 @@ _.extend(Roles, { Roles._checkRoleName(roleName); - options = _.defaults(options, { + options = Object.assign({ unlessExists: false - }); + }, options); var result = Meteor.roles.upsert({_id: roleName}, {$setOnInsert: {children: []}}); @@ -111,11 +111,11 @@ _.extend(Roles, { roles: 1 } } - }).forEach(function (user, index, cursor) { + }).fetch().forEach(function (user, index, cursor) { // role can be assigned multiple times to the user, for multiple scopes // we have to remove the role for each of those scopes - roles = _.filter(user.roles, Roles._roleMatcher(roleName)); - _.each(roles, function (role) { + roles = user.roles.filter(Roles._roleMatcher(roleName)); + roles.forEach(function (role) { Roles._removeUserFromRole(user, roleName, { scope: role.scope, // we want to remove the role in any case @@ -202,9 +202,9 @@ _.extend(Roles, { */ addRolesToParent: function (rolesNames, parentName) { // ensure arrays - if (!_.isArray(rolesNames)) rolesNames = [rolesNames]; + if (!Array.isArray(rolesNames)) rolesNames = [rolesNames]; - _.each(rolesNames, function (roleName) { + rolesNames.forEach(function (roleName) { Roles._addRoleToParent(roleName, parentName); }); }, @@ -236,22 +236,21 @@ _.extend(Roles, { } // detect cycles - alreadyCheckedRoles = []; - rolesToCheck = _.pluck(role.children, '_id'); - while (rolesToCheck.length) { - checkRoleName = rolesToCheck.pop(); + alreadyCheckedRoles = new Set(); + rolesToCheck = new Set(role.children.map(r => r._id)); + rolesToCheck.forEach(checkRoleName => { if (checkRoleName === parentName) { throw new Error("Roles '" + roleName + "' and '" + parentName + "' would form a cycle."); } - alreadyCheckedRoles.push(checkRoleName); - checkRole = Meteor.roles.findOne({_id: checkRoleName}); + alreadyCheckedRoles.add(checkRoleName); - // This should not happen, but this is a problem to address at some other time. - if (!checkRole) continue; + checkRole = Meteor.roles.findOne({_id: checkRoleName}); - rolesToCheck = _.union(rolesToCheck, _.difference(_.pluck(checkRole.children, '_id'), alreadyCheckedRoles)); - } + if (checkRole) { + checkRole.children.map(r => r._id).filter(r => !alreadyCheckedRoles.has(r)).forEach(r => rolesToCheck.add(r)); + } + }) count = Meteor.roles.update({ _id: parentName, @@ -278,11 +277,11 @@ _.extend(Roles, { roles: 1 } } - }).forEach(function (user, index, cursor) { + }).fetch().forEach(function (user, index, cursor) { // parent role can be assigned multiple times to the user, for multiple scopes // we have to assign a new subrole for each of those scopes - parentRoles = _.filter(user.roles, Roles._roleMatcher(parentName)); - _.each(parentRoles, function (parentRole) { + parentRoles = user.roles.filter(Roles._roleMatcher(parentName)); + parentRoles.forEach(function (parentRole) { Roles._addUserToRole(user, roleName, { scope: parentRole.scope, // we are assigning a subrole, so we set it as unassigned, @@ -306,9 +305,9 @@ _.extend(Roles, { */ removeRolesFromParent: function (rolesNames, parentName) { // ensure arrays - if (!_.isArray(rolesNames)) rolesNames = [rolesNames]; + if (!Array.isArray(rolesNames)) rolesNames = [rolesNames]; - _.each(rolesNames, function (roleName) { + rolesNames.forEach(function (roleName) { Roles._removeRoleFromParent(roleName, parentName); }); }, @@ -358,11 +357,11 @@ _.extend(Roles, { roles: 1 } } - }).forEach(function (user, index, cursor) { + }).fetch().forEach(function (user, index, cursor) { // parent role can be assigned multiple times to the user, for multiple scopes // we have to remove the subrole for each of those scopes - parentRoles = _.filter(user.roles, Roles._roleMatcher(parentName)); - _.each(parentRoles, function (parentRole) { + parentRoles = user.roles.filter(Roles._roleMatcher(parentName)); + parentRoles.forEach(function (parentRole) { Roles._removeUserFromRole(user, roleName, { scope: parentRole.scope, // but we want to remove it only if it was not also explicitly assigned @@ -403,22 +402,22 @@ _.extend(Roles, { options = Roles._normalizeOptions(options); // ensure arrays - if (!_.isArray(users)) users = [users]; - if (!_.isArray(roles)) roles = [roles]; + if (!Array.isArray(users)) users = [users]; + if (!Array.isArray(roles)) roles = [roles]; Roles._checkScopeName(options.scope); - options = _.defaults(options, { + options = Object.assign({ ifExists: false, // internal option, should not be used publicly because it will break assumptions // in te code; publicly, you can only add users to an assigned role // should the role be set as assigned, default is `true`; `null` is the same as `false`, // only that it does not force the value to `false` if the role is already assigned _assigned: true - }); + }, options); - _.each(users, function (user) { - _.each(roles, function (role) { + users.forEach(function (user) { + roles.forEach(function (role) { Roles._addUserToRole(user, role, options); }); }); @@ -454,22 +453,22 @@ _.extend(Roles, { options = Roles._normalizeOptions(options); // ensure arrays - if (!_.isArray(users)) users = [users]; - if (!_.isArray(roles)) roles = [roles]; + if (!Array.isArray(users)) users = [users]; + if (!Array.isArray(roles)) roles = [roles]; Roles._checkScopeName(options.scope); - options = _.defaults(options, { + options = Object.assign({ ifExists: false, // internal option, should not be used publicly because it will break assumptions // in te code; publicly, you can only add users to an assigned role // should the role be set as assigned, default is `true`; `null` is the same as `false`, // only that it does not force the value to `false` if the role is already assigned _assigned: true - }); + }, options); - _.each(users, function (user) { - if (_.isObject(user)) { + users.forEach(function (user) { + if (typeof user === 'object') { id = user._id; } else { @@ -479,7 +478,7 @@ _.extend(Roles, { Meteor.users.update(id, {$pull: {roles: {scope: options.scope}}}); // and then add all - _.each(roles, function (role) { + roles.forEach(function (role) { Roles._addUserToRole(user, role, options); }); }); @@ -511,7 +510,7 @@ _.extend(Roles, { Roles._checkRoleName(roleName); Roles._checkScopeName(options.scope); - if (_.isObject(user)) { + if (typeof user === 'object') { id = user._id; } else { @@ -597,9 +596,9 @@ _.extend(Roles, { scope: options.scope }]; - _.each(role.children, function (child) { + role.children.forEach(function (child) { // subroles are set as unassigned, but only if they do not already exist - setRoles = setRoles.concat(Roles._addUserToRole(user, child._id, _.extend({}, options, {_assigned: null}))); + setRoles = setRoles.concat(Roles._addUserToRole(user, child._id, Object.assign({}, options, {_assigned: null}))); }); return setRoles; @@ -629,21 +628,23 @@ _.extend(Roles, { options = Roles._normalizeOptions(options); // ensure arrays - if (!_.isArray(users)) users = [users]; - if (!_.isArray(roles)) roles = [roles]; + if (!Array.isArray(users)) users = [users]; + if (!Array.isArray(roles)) roles = [roles]; Roles._checkScopeName(options.scope); - options = _.defaults(options, { + options = Object.assign({ // internal option, should not be used publicly because it will break assumptions // in te code; publicly, you can only remove users from an assigned role // when should the role be removed, default is `true` which means only when it is assigned, // `false` means when it is not assigned, and `null` means always _assigned: true - }); + }, options); + + users.forEach(function (user) { + if (!user) return; - _.each(users, function (user) { - _.each(roles, function (role) { + roles.forEach(function (role) { Roles._removeUserFromRole(user, role, options); }); @@ -681,7 +682,7 @@ _.extend(Roles, { Roles._checkRoleName(roleName); Roles._checkScopeName(options.scope); - if (_.isObject(user)) { + if (typeof user === 'object') { id = user._id; } else { @@ -714,9 +715,9 @@ _.extend(Roles, { // role does not exist, we do not anything more if (!role) return; - _.each(role.children, function (child) { + role.children.forEach(function (child) { // if a child role has been assigned explicitly, we do not remove it - Roles._removeUserFromRole(user, child._id, _.extend({}, options, {_assigned: false})); + Roles._removeUserFromRole(user, child._id, Object.assign({}, options, {_assigned: false})); }); }, @@ -742,10 +743,10 @@ _.extend(Roles, { user = Roles._resolveUser(user, true); // only assigned roles - roles = _.filter(user.roles, Roles._onlyAssignedMatcher()); + roles = user.roles.filter(Roles._onlyAssignedMatcher()); setRoles = []; - _.each(roles, function (role) { + roles.forEach(function (role) { setRoles = setRoles.concat(Roles._addUserToRole(user, role._id, { scope: role.scope, _assigned: role.assigned, // this is true @@ -758,7 +759,7 @@ _.extend(Roles, { Meteor.users.update(user._id, { $pull: { roles: { - $nor: _.map(setRoles, function (role) {return _.pick(role, '_id', 'scope')}) + $nor: setRoles.map(function (role) { return {_id: role._id, scope: role.scope } }) } } }); @@ -804,26 +805,27 @@ _.extend(Roles, { options = Roles._normalizeOptions(options); // ensure array to simplify code - if (!_.isArray(roles)) roles = [roles]; + if (!Array.isArray(roles)) roles = [roles]; if (!roles.length) return false; Roles._checkScopeName(options.scope); - options = _.defaults(options, { + options = Object.assign({ anyScope: false - }); + }, options); if (!user) return false; - if (_.isObject(user)) { - if (_.has(user, 'roles')) { - return _.some(roles, function (role) { + if (typeof user === 'object') { + if ('roles' in user) { + return roles.some(function (role) { + const roles = user.roles || []; if (options.anyScope) { - return _.some(user.roles || [], Roles._roleMatcher(role)); + return roles.some(Roles._roleMatcher(role)); } else { - return _.some(user.roles || [], Roles._roleAndScopeMatcher(role, options.scope)); + return roles.some(Roles._roleAndScopeMatcher(role, options.scope)); } }) } else { @@ -888,32 +890,30 @@ _.extend(Roles, { Roles._checkScopeName(options.scope); - options = _.defaults(options, { + options = Object.assign({ fullObjects: false, onlyAssigned: false, anyScope: false - }); + }, options); user = Roles._resolveUser(user); if (!user) return []; - if (options.anyScope) { - roles = user.roles || []; - } - else { - roles = _.filter(user.roles || [], Roles._scopeMatcher(options.scope)); + roles = user.roles || []; + if (!options.anyScope) { + roles = roles.filter(Roles._scopeMatcher(options.scope)); } if (options.onlyAssigned) { - roles = _.filter(roles, Roles._onlyAssignedMatcher()); + roles = roles.filter(Roles._onlyAssignedMatcher()); } if (options.fullObjects) { return roles; } - return _.uniq(_.pluck(roles, '_id')); + return [...new Set(roles.map(r => r._id))]; }, /** @@ -988,14 +988,14 @@ _.extend(Roles, { options = Roles._normalizeOptions(options); // ensure array to simplify code - if (!_.isArray(roles)) roles = [roles]; + if (!Array.isArray(roles)) roles = [roles]; Roles._checkScopeName(options.scope); - options = _.defaults(options, { + options = Object.assign({ queryOptions: queryOptions || {}, anyScope: false - }); + }, options); if (options.anyScope) { query = { @@ -1058,22 +1058,22 @@ _.extend(Roles, { var scopes; // ensure array to simplify code - if (roles && !_.isArray(roles)) roles = [roles]; + if (roles && !Array.isArray(roles)) roles = [roles]; user = Roles._resolveUser(user); if (!user) return []; scopes = []; - _.each(user.roles || [], function (userRole) { + (user.roles || []).forEach(function (userRole) { // == used on purpose. if (userRole.scope == null) return; - if (roles && !_.contains(roles, userRole._id)) return; + if (roles && !roles.includes(userRole._id)) return; scopes.push(userRole.scope); }); - return _.uniq(scopes); + return [...new Set(scopes)]; }, /** @@ -1143,11 +1143,13 @@ _.extend(Roles, { */ _resolveUser: function (user, force) { // TODO: We could use $elemMatch to limit returned fields here. - if (!_.isObject(user)) { + if (!user) { + return null; + } else if (typeof user !== 'object') { user = Meteor.users.findOne( {_id: user}, {fields: {roles: 1}}); - } else if (force || !_.has(user, 'roles')) { + } else if (force || !('roles' in user)) { user = Meteor.users.findOne( {_id: user._id}, {fields: {roles: 1}}); @@ -1183,7 +1185,7 @@ _.extend(Roles, { return function (userRole) { // == used on purpose in "userRole.scope == null" return (userRole._id === roleName && userRole.scope === scope) || - (userRole._id === roleName && (!_.has(userRole, 'scope') || userRole.scope == null)); + (userRole._id === roleName && (!('scope' in userRole) || userRole.scope == null)); }; }, @@ -1199,7 +1201,7 @@ _.extend(Roles, { return function (userRole) { // == used on purpose in "userRole.scope == null" return (userRole.scope === scope) || - (!_.has(userRole, 'scope') || userRole.scope == null); + (!('scope' in userRole) || userRole.scope == null); }; }, @@ -1225,7 +1227,7 @@ _.extend(Roles, { * @static */ _checkRoleName: function (roleName) { - if (!roleName || !_.isString(roleName) || Roles._trim(roleName) !== roleName) { + if (!roleName || typeof roleName !== 'string' || Roles._trim(roleName) !== roleName) { throw new Error("Invalid role name '" + roleName + "'."); } }, @@ -1240,9 +1242,9 @@ _.extend(Roles, { * @static */ _normalizeOptions: function (options) { - options = _.isUndefined(options) ? {} : options; + options = options === undefined ? {} : options; - if (options === null || _.isString(options)) { + if (options === null || typeof options === 'string') { options = {scope: options}; } @@ -1281,7 +1283,7 @@ _.extend(Roles, { _checkScopeName: function (scopeName) { if (scopeName === null) return; - if (!scopeName || !_.isString(scopeName) || Roles._trim(scopeName) !== scopeName) { + if (!scopeName || typeof scopeName !== 'string' || Roles._trim(scopeName) !== scopeName) { throw new Error("Invalid scope name '" + scopeName + "'."); } }, @@ -1300,7 +1302,6 @@ _.extend(Roles, { return string.replace(/^\s+|\s+$/g, ''); } } - -}); // end _.extend(Roles ...) +}); }()); diff --git a/roles/roles_server.js b/roles/roles_server.js index 72fd941..a4b2e82 100644 --- a/roles/roles_server.js +++ b/roles/roles_server.js @@ -23,7 +23,7 @@ Meteor.publish('_roles', function () { {fields: fields}); }); -_.extend(Roles, { +Object.assign(Roles, { /** * @method _isNewRole * @param {Object} role `Meteor.roles` document. @@ -34,7 +34,7 @@ _.extend(Roles, { * @static */ _isNewRole: function (role) { - return !_.has(role, 'name') && _.has(role, 'children'); + return !('name' in role) && 'children' in role; }, /** @@ -47,7 +47,7 @@ _.extend(Roles, { * @static */ _isOldRole: function (role) { - return _.has(role, 'name') && !_.has(role, 'children'); + return 'name' in role && !('children' in role); }, /** @@ -60,7 +60,7 @@ _.extend(Roles, { * @static */ _isNewField: function (roles) { - return _.isArray(roles) && _.isObject(roles[0]); + return Array.isArray(roles) && (typeof roles[0] === 'object'); }, /** @@ -73,7 +73,7 @@ _.extend(Roles, { * @static */ _isOldField: function (roles) { - return (_.isArray(roles) && _.isString(roles[0])) || (_.isObject(roles) && !_.isArray(roles)); + return (Array.isArray(roles) && (typeof roles[0] === 'string')) || ((typeof roles === 'object') && !Array.isArray(roles)); }, /** @@ -85,7 +85,7 @@ _.extend(Roles, { * @static */ _convertToNewRole: function (oldRole) { - if (!_.isString(oldRole.name)) throw new Error("Role name '" + oldRole.name + "' is not a string."); + if (!(typeof oldRole.name === 'string')) throw new Error("Role name '" + oldRole.name + "' is not a string."); return { _id: oldRole.name, @@ -102,7 +102,7 @@ _.extend(Roles, { * @static */ _convertToOldRole: function (newRole) { - if (!_.isString(newRole._id)) throw new Error("Role name '" + newRole._id + "' is not a string."); + if (!(typeof newRole._id === 'string')) throw new Error("Role name '" + newRole._id + "' is not a string."); return { name: newRole._id @@ -120,9 +120,9 @@ _.extend(Roles, { */ _convertToNewField: function (oldRoles, convertUnderscoresToDots) { var roles = []; - if (_.isArray(oldRoles)) { - _.each(oldRoles, function (role, index) { - if (!_.isString(role)) throw new Error("Role '" + role + "' is not a string."); + if (Array.isArray(oldRoles)) { + oldRoles.forEach(function (role, index) { + if (!(typeof role === 'string')) throw new Error("Role '" + role + "' is not a string."); roles.push({ _id: role, @@ -131,8 +131,8 @@ _.extend(Roles, { }) }); } - else if (_.isObject(oldRoles)) { - _.each(oldRoles, function (rolesArray, group) { + else if (typeof oldRoles === 'object') { + Object.entries(oldRoles).forEach(([group, rolesArray]) => { if (group === '__global_roles__') { group = null; } @@ -141,8 +141,8 @@ _.extend(Roles, { group = group.replace(/_/g, '.'); } - _.each(rolesArray, function (role, index) { - if (!_.isString(role)) throw new Error("Role '" + role + "' is not a string."); + rolesArray.forEach(function (role) { + if (!(typeof role === 'string')) throw new Error("Role '" + role + "' is not a string."); roles.push({ _id: role, @@ -174,8 +174,8 @@ _.extend(Roles, { roles = []; } - _.each(newRoles, function (userRole, index) { - if (!_.isObject(userRole)) throw new Error("Role '" + userRole + "' is not an object."); + newRoles.forEach(function (userRole) { + if (!(typeof userRole === 'object')) throw new Error("Role '" + userRole + "' is not an object."); // We assume that we are converting back a failed migration, so values can only be // what were valid values in 1.0. So no group names starting with $ and no subroles. diff --git a/roles/tests/client.js b/roles/tests/client.js index 34444d0..6c6e802 100644 --- a/roles/tests/client.js +++ b/roles/tests/client.js @@ -48,8 +48,8 @@ var user = users[username]; // test using user object rather than userId to avoid mocking - _.each(roles, function (role) { - var expected = _.contains(expectedRoles, role), + roles.forEach(function (role) { + var expected = expectedRoles.includes(role), msg = username + ' expected to have \'' + role + '\' permission but does not', nmsg = username + ' had un-expected permission ' + role; @@ -68,7 +68,7 @@ }; Tinytest.add( - 'roles - can check current users roles via template helper', + 'roles - can check current users roles via template helper', function (test) { var isInRole, expected, @@ -86,7 +86,7 @@ expected = true; actual = isInRole('admin, editor'); test.equal(actual, expected); - + expected = true; actual = isInRole('admin'); test.equal(actual, expected); @@ -97,20 +97,20 @@ }); Tinytest.add( - 'roles - can check if user is in role', + 'roles - can check if user is in role', function (test) { testUser(test, 'eve', ['admin', 'editor']); }); Tinytest.add( - 'roles - can check if user is in role by group', + 'roles - can check if user is in role by group', function (test) { testUser(test, 'bob', ['user'], 'group1'); testUser(test, 'bob', ['editor'], 'group2'); }); Tinytest.add( - 'roles - can check if user is in role with Roles.GLOBAL_GROUP', + 'roles - can check if user is in role with Roles.GLOBAL_GROUP', function (test) { testUser(test, 'joe', ['admin']); testUser(test, 'joe', ['admin'], Roles.GLOBAL_GROUP); diff --git a/roles/tests/server.js b/roles/tests/server.js index e6f6f58..557667d 100644 --- a/roles/tests/server.js +++ b/roles/tests/server.js @@ -26,7 +26,7 @@ function testUser (test, username, expectedRoles, scope) { var userId = users[username], userObj = Meteor.users.findOne({_id: userId}); - + // check using user ids (makes db calls) _innerTest(test, userId, username, expectedRoles, scope); @@ -36,8 +36,8 @@ function _innerTest (test, userParam, username, expectedRoles, scope) { // test that user has only the roles expected and no others - _.each(roles, function (role) { - var expected = _.contains(expectedRoles, role), + roles.forEach(function (role) { + var expected = expectedRoles.includes(role), msg = username + ' expected to have \'' + role + '\' role but does not', nmsg = username + ' had the following un-expected role: ' + role; @@ -49,18 +49,42 @@ }) } + var deepEqual = function (x, y) { + if (x === y) { + return true; + } + else if ((typeof x == "object" && x != null) && (typeof y == "object" && y != null)) { + if (Object.keys(x).length != Object.keys(y).length) + return false; + + for (var prop in x) { + if (y.hasOwnProperty(prop)) + { + if (! deepEqual(x[prop], y[prop])) + return false; + } + else + return false; + } + + return true; + } + else + return false; + } + function itemsEqual (test, actual, expected) { actual = actual || []; expected = expected || []; - function intersectionObjects(/*args*/) { + function intersectionObjects(...args) { var array, rest; - array = arguments[0]; - rest = 2 <= arguments.length ? _.toArray(arguments).slice(1) : []; - return _.filter(_.uniq(array), function (item) { - return _.every(rest, function (other) { - return _.any(other, function (element) { - return _.isEqual(element, item); + array = args[0]; + rest = 2 <= args.length ? args.slice(1) : []; + return [...new Set(array)].filter(function (item) { + return rest.every(function (other) { + return other.some(function (element) { + return deepEqual(element, item); }); }); }); @@ -79,7 +103,7 @@ } Tinytest.add( - 'roles - can create and delete roles', + 'roles - can create and delete roles', function (test) { reset(); @@ -101,7 +125,7 @@ }); Tinytest.add( - 'roles - can\'t create duplicate roles', + 'roles - can\'t create duplicate roles', function (test) { reset(); @@ -111,7 +135,7 @@ }); Tinytest.add( - 'roles - can\'t create role with empty names', + 'roles - can\'t create role with empty names', function (test) { reset(); @@ -161,7 +185,7 @@ }); Tinytest.add( - 'roles - can check if user is in role', + 'roles - can check if user is in role', function (test) { reset(); @@ -309,7 +333,7 @@ }); Tinytest.add( - 'roles - can check if non-existant user is in role', + 'roles - can check if non-existant user is in role', function (test) { reset(); @@ -317,16 +341,16 @@ }); Tinytest.add( - 'roles - can check if null user is in role', + 'roles - can check if null user is in role', function (test) { var user = null; reset(); - + test.isFalse(Roles.userIsInRole(user, 'admin')); }); Tinytest.add( - 'roles - can check user against several roles at once', + 'roles - can check user against several roles at once', function (test) { var user; reset(); @@ -342,7 +366,7 @@ }); Tinytest.add( - 'roles - can\'t add non-existent user to role', + 'roles - can\'t add non-existent user to role', function (test) { reset(); @@ -386,7 +410,7 @@ }); Tinytest.add( - 'roles - can add individual users to roles', + 'roles - can add individual users to roles', function (test) { reset(); @@ -439,7 +463,7 @@ }); Tinytest.add( - 'roles - can add user to roles via user object', + 'roles - can add user to roles via user object', function (test) { reset(); @@ -464,7 +488,7 @@ }); Tinytest.add( - 'roles - can add user to roles multiple times', + 'roles - can add user to roles multiple times', function (test) { reset(); @@ -512,7 +536,7 @@ }); Tinytest.add( - 'roles - can add multiple users to roles', + 'roles - can add multiple users to roles', function (test) { reset(); @@ -565,7 +589,7 @@ }); Tinytest.add( - 'roles - can remove individual users from roles', + 'roles - can remove individual users from roles', function (test) { reset(); @@ -603,7 +627,7 @@ }); Tinytest.add( - 'roles - can remove users from roles via user object', + 'roles - can remove users from roles via user object', function (test) { reset(); @@ -612,7 +636,7 @@ var eve = Meteor.users.findOne({_id: users.eve}), bob = Meteor.users.findOne({_id: users.bob}); - + // remove user role - one user Roles.addUsersToRoles([eve, bob], ['editor', 'user']); testUser(test, 'eve', ['editor', 'user']); @@ -679,7 +703,7 @@ }); Tinytest.add( - 'roles - can remove multiple users from roles', + 'roles - can remove multiple users from roles', function (test) { reset(); @@ -735,7 +759,7 @@ }); Tinytest.add( - 'roles - can set user roles', + 'roles - can set user roles', function (test) { reset(); @@ -746,7 +770,7 @@ var eve = Meteor.users.findOne({_id: users.eve}), bob = Meteor.users.findOne({_id: users.bob}), joe = Meteor.users.findOne({_id: users.joe}); - + Roles.setUserRoles([users.eve, bob], ['editor', 'user']); testUser(test, 'eve', ['editor', 'user']); testUser(test, 'bob', ['editor', 'user']); @@ -786,7 +810,7 @@ var eve = Meteor.users.findOne({_id: users.eve}), bob = Meteor.users.findOne({_id: users.bob}), joe = Meteor.users.findOne({_id: users.joe}); - + Roles.setUserRoles([users.eve, users.bob], ['editor', 'user'], 'scope1'); Roles.setUserRoles([users.bob, users.joe], ['admin'], 'scope2'); testUser(test, 'eve', ['editor', 'user'], 'scope1'); @@ -823,8 +847,8 @@ testUser(test, 'bob', ['admin', 'editor'], 'scope2'); testUser(test, 'joe', ['editor'], 'scope2'); - test.isTrue(_.contains(_.pluck(Roles.getRolesForUser(users.bob, {anyScope: true, fullObjects: true}), 'scope'), 'scope1')); - test.isFalse(_.contains(_.pluck(Roles.getRolesForUser(users.joe, {anyScope: true, fullObjects: true}), 'scope'), 'scope1')); + test.isTrue(Roles.getRolesForUser(users.bob, {anyScope: true, fullObjects: true}).map(r => r.scope).includes('scope1')); + test.isFalse(Roles.getRolesForUser(users.joe, {anyScope: true, fullObjects: true}).map(r => r.scope).includes('scope1')); Roles.setUserRoles([bob, users.joe], [], 'scope1'); testUser(test, 'eve', ['user'], 'scope1'); @@ -835,8 +859,8 @@ testUser(test, 'joe', ['editor'], 'scope2'); // When roles in a given scope are removed, we do not want any dangling database content for that scope. - test.isFalse(_.contains(_.pluck(Roles.getRolesForUser(users.bob, {anyScope: true, fullObjects: true}), 'scope'), 'scope1')); - test.isFalse(_.contains(_.pluck(Roles.getRolesForUser(users.joe, {anyScope: true, fullObjects: true}), 'scope'), 'scope1')); + test.isFalse(Roles.getRolesForUser(users.bob, {anyScope: true, fullObjects: true}).map(r => r.scope).includes('scope1')); + test.isFalse(Roles.getRolesForUser(users.joe, {anyScope: true, fullObjects: true}).map(r => r.scope).includes('scope1')); }); Tinytest.add( @@ -850,7 +874,7 @@ var eve = Meteor.users.findOne({_id: users.eve}), bob = Meteor.users.findOne({_id: users.bob}), joe = Meteor.users.findOne({_id: users.joe}); - + Roles.addUsersToRoles(eve, 'admin', Roles.GLOBAL_SCOPE); testUser(test, 'eve', ['admin'], 'scope1'); testUser(test, 'eve', ['admin']); @@ -862,25 +886,25 @@ Tinytest.add( - 'roles - can get all roles', + 'roles - can get all roles', function (test) { reset(); - _.each(roles, function (role) { + roles.forEach(function (role) { Roles.createRole(role); }); // compare roles, sorted alphabetically - var expected = _.clone(roles), - actual = _.pluck(Roles.getAllRoles().fetch(), '_id'); + var expected = roles, + actual = Roles.getAllRoles().fetch().map(r => r._id); test.equal(actual, expected); - test.equal(_.pluck(Roles.getAllRoles({sort: {_id: -1}}).fetch(), '_id'), expected.reverse()); + test.equal(Roles.getAllRoles({sort: {_id: -1}}).fetch().map(r => r._id), expected.reverse()); }); Tinytest.add( - 'roles - can\'t get roles for non-existant user', + 'roles - can\'t get roles for non-existant user', function (test) { reset(); test.equal(Roles.getRolesForUser('1'), []); @@ -888,7 +912,7 @@ }); Tinytest.add( - 'roles - can get all roles for user', + 'roles - can get all roles for user', function (test) { reset(); @@ -1133,7 +1157,7 @@ test.equal(Roles.getRolesForUser(userObj, 'scope1'), ['editor']); test.equal(Roles.getRolesForUser(userObj), ['editor']); }); - + Tinytest.add( 'roles - can get all scopes for user', function (test) { @@ -1156,7 +1180,7 @@ userObj = Meteor.users.findOne({_id: userId}); test.equal(Roles.getScopesForUser(userObj), ['scope1', 'scope2']); }); - + Tinytest.add( 'roles - can get all scopes for user by role', function (test) { @@ -1183,7 +1207,7 @@ test.equal(Roles.getScopesForUser(userObj, 'editor'), ['scope1', 'scope2']); test.equal(Roles.getScopesForUser(userObj, 'admin'), []); }); - + Tinytest.add( 'roles - getScopesForUser returns [] when not using scopes', function (test) { @@ -1249,7 +1273,7 @@ test.equal(Roles.getScopesForUser(userObj, ['editor', 'moderator']), ['group1', 'group2', 'group3']); test.equal(Roles.getScopesForUser(userObj, ['user', 'moderator']), ['group2', 'group3']); }); - + Tinytest.add( 'roles - getting all scopes for user does not include GLOBAL_SCOPE', function (test) { @@ -1288,7 +1312,7 @@ Tinytest.add( - 'roles - can get all users in role', + 'roles - can get all users in role', function (test) { reset(); @@ -1300,7 +1324,7 @@ Roles.addUsersToRoles([users.bob, users.joe], ['editor']); var expected = [users.eve, users.joe], - actual = _.pluck(Roles.getUsersInRole('admin').fetch(), '_id'); + actual = Roles.getUsersInRole('admin').fetch().map(r => r._id); itemsEqual(test, actual, expected); }); @@ -1317,22 +1341,22 @@ Roles.addUsersToRoles([users.bob, users.joe], ['admin'], 'scope2'); var expected = [users.eve, users.joe], - actual = _.pluck(Roles.getUsersInRole('admin', 'scope1').fetch(), '_id'); + actual = Roles.getUsersInRole('admin', 'scope1').fetch().map(r => r._id); itemsEqual(test, actual, expected); expected = [users.eve, users.joe]; - actual = _.pluck(Roles.getUsersInRole('admin', {scope: 'scope1'}).fetch(), '_id'); + actual = Roles.getUsersInRole('admin', {scope: 'scope1'}).fetch().map(r => r._id); itemsEqual(test, actual, expected); expected = [users.eve, users.bob, users.joe]; - actual = _.pluck(Roles.getUsersInRole('admin', {anyScope: true}).fetch(), '_id'); + actual = Roles.getUsersInRole('admin', {anyScope: true}).fetch().map(r => r._id); itemsEqual(test, actual, expected); - actual = _.pluck(Roles.getUsersInRole('admin').fetch(), '_id'); + actual = Roles.getUsersInRole('admin').fetch().map(r => r._id); test.equal(actual, []); }); - + Tinytest.add( 'roles - can get all users in role by scope including Roles.GLOBAL_SCOPE', function (test) { @@ -1345,22 +1369,22 @@ Roles.addUsersToRoles([users.bob, users.joe], ['admin'], 'scope2'); var expected = [users.eve], - actual = _.pluck(Roles.getUsersInRole('admin', 'scope1').fetch(), '_id'); + actual = Roles.getUsersInRole('admin', 'scope1').fetch().map(r => r._id); itemsEqual(test, actual, expected); expected = [users.eve, users.bob, users.joe]; - actual = _.pluck(Roles.getUsersInRole('admin', 'scope2').fetch(), '_id'); + actual = Roles.getUsersInRole('admin', 'scope2').fetch().map(r => r._id); itemsEqual(test, actual, expected); expected = [users.eve]; - actual = _.pluck(Roles.getUsersInRole('admin').fetch(), '_id'); + actual = Roles.getUsersInRole('admin').fetch().map(r => r._id); itemsEqual(test, actual, expected); expected = [users.eve, users.bob, users.joe]; - actual = _.pluck(Roles.getUsersInRole('admin', {anyScope: true}).fetch(), '_id'); + actual = Roles.getUsersInRole('admin', {anyScope: true}).fetch().map(r => r._id); itemsEqual(test, actual, expected); }); @@ -1436,7 +1460,7 @@ testUser(test, 'bob', ['admin'], 'scope2'); testUser(test, 'bob', ['admin'], 'scope1'); }); - + Tinytest.add( 'roles - Roles.GLOBAL_SCOPE also checked when scope not specified', function (test) {