From b0dd807388f547e27c3a073cccc26bfc27afb2fa Mon Sep 17 00:00:00 2001 From: andy lash Date: Sun, 27 Apr 2014 14:06:10 -0700 Subject: [PATCH 01/16] added impersonation and security checks --- client/accounts_admin.html | 6 ++++- client/accounts_admin.js | 16 ++++++++----- client/impersonate_account_modal.html | 24 +++++++++++++++++++ client/impersonate_account_modal.js | 30 ++++++++++++++++++++++++ libs/user_query.js | 2 +- package.js | 4 +++- server/methods.js | 33 ++++++++++++++++++++++++--- server/publish.js | 3 ++- server/startup.js | 2 +- 9 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 client/impersonate_account_modal.html create mode 100644 client/impersonate_account_modal.js diff --git a/client/accounts_admin.html b/client/accounts_admin.html index 3366e03..fd4e58a 100644 --- a/client/accounts_admin.html +++ b/client/accounts_admin.html @@ -1,4 +1,5 @@ \ No newline at end of file + {{> impersonateAccountModal}} + + diff --git a/client/accounts_admin.js b/client/accounts_admin.js index 785f781..a510724 100644 --- a/client/accounts_admin.js +++ b/client/accounts_admin.js @@ -39,20 +39,24 @@ var setUserFilter = _.throttle(function(template) { Template.accountsAdmin.events({ 'keyup .search-input-filter': function(event, template) { - setUserFilter(template); - return false; + setUserFilter(template); + return false; }, 'click .glyphicon-trash': function(event, template) { - Session.set('userInScope', this); + Session.set('userInScope', this); }, 'click .glyphicon-info-sign': function(event, template) { - Session.set('userInScope', this); + Session.set('userInScope', this); }, 'click .glyphicon-pencil': function(event, template) { - Session.set('userInScope', this); + Session.set('userInScope', this); + }, + + 'click .glyphicon-eye-open': function(event, template) { + Session.set('userInScope', this); } }); @@ -68,4 +72,4 @@ Template.accountsAdmin.rendered = function() { searchElement[0].focus(); searchElement[0].setSelectionRange(pos, pos); -}; \ No newline at end of file +}; diff --git a/client/impersonate_account_modal.html b/client/impersonate_account_modal.html new file mode 100644 index 0000000..a6e3dd0 --- /dev/null +++ b/client/impersonate_account_modal.html @@ -0,0 +1,24 @@ + + + diff --git a/client/impersonate_account_modal.js b/client/impersonate_account_modal.js new file mode 100644 index 0000000..e6d8b75 --- /dev/null +++ b/client/impersonate_account_modal.js @@ -0,0 +1,30 @@ +Template.impersonateAccountModalInner.helpers({ + userInScope: function () { + return Session.get('userInScope'); + }, +}); + + +Template.impersonateAccountModalInner.events({ + 'click .btn-danger': function (event, template) { + var self = this; + Meteor.call('impersonateUser', self._id, function (error) { + if (error) { + // optionally use a meteor errors package + if (typeof Errors === "undefined") + Log.error('Error: ' + error.reason); + else { + Errors. + throw (error.reason); + } + } + // $("#impersonateaccount").modal("hide"); + // event.stopImmediatePropagation(); + // event.preventDefault(); + Meteor.connection.setUserId(self._id); + $('body').removeClass('modal-open'); + $('.modal-backdrop').remove(); + Router.go("plansList"); + }); + } +}); diff --git a/libs/user_query.js b/libs/user_query.js index a934433..ce97a2c 100644 --- a/libs/user_query.js +++ b/libs/user_query.js @@ -18,4 +18,4 @@ filteredUserQuery = function(userId, filter) { users = Meteor.users.find({}, {sort: {emails: 1}, limit: queryLimit}); } return users; -}; \ No newline at end of file +}; diff --git a/package.js b/package.js index 46c822a..12d20f5 100644 --- a/package.js +++ b/package.js @@ -20,10 +20,12 @@ Package.on_use(function (api, where) { api.add_files('client/update_account_modal.js', 'client'); api.add_files('client/update_roles_modal.html', 'client'); api.add_files('client/update_roles_modal.js', 'client'); + api.add_files('client/impersonate_account_modal.html', 'client'); + api.add_files('client/impersonate_account_modal.js', 'client'); api.add_files('style/style.css', 'client'); api.add_files('server/startup.js', 'server'); api.add_files('server/publish.js', 'server'); api.add_files('server/methods.js', 'server'); -}); \ No newline at end of file +}); diff --git a/server/methods.js b/server/methods.js index 165c4af..c8fab07 100644 --- a/server/methods.js +++ b/server/methods.js @@ -1,17 +1,20 @@ Meteor.methods({ deleteUser: function(userId) { + check(userId, String); var user = Meteor.user(); if (!user || !Roles.userIsInRole(user, ['admin'])) throw new Meteor.Error(401, "You need to be an admin to delete a user."); if (user._id == userId) throw new Meteor.Error(422, 'You can\'t delete yourself.'); - + // remove the user Meteor.users.remove(userId); }, addUserRole: function(userId, role) { + check(userId, String); + check(role, String); var user = Meteor.user(); if (!user || !Roles.userIsInRole(user, ['admin'])) throw new Meteor.Error(401, "You need to be an admin to update a user."); @@ -32,6 +35,8 @@ Meteor.methods({ }, removeUserRole: function(userId, role) { + check(userId, String); + check(role, String); var user = Meteor.user(); if (!user || !Roles.userIsInRole(user, ['admin'])) throw new Meteor.Error(401, "You need to be an admin to update a user."); @@ -51,6 +56,7 @@ Meteor.methods({ }, addRole: function(role) { + check(role, String); var user = Meteor.user(); if (!user || !Roles.userIsInRole(user, ['admin'])) throw new Meteor.Error(401, "You need to be an admin to update a user."); @@ -63,6 +69,7 @@ Meteor.methods({ }, removeRole: function(role) { + check(role, String); var user = Meteor.user(); if (!user || !Roles.userIsInRole(user, ['admin'])) throw new Meteor.Error(401, "You need to be an admin to update a user."); @@ -91,6 +98,10 @@ Meteor.methods({ }, updateUserInfo: function(id, property, value) { + check(id, String); + check(property, String); + //Giving the value a range of possible safe values + check(value, Match.OneOf(String, Number, Boolean, Date, undefined, null)); var user = Meteor.user(); if (!user || !Roles.userIsInRole(user, ['admin'])) throw new Meteor.Error(401, "You need to be an admin to update a user."); @@ -102,5 +113,21 @@ Meteor.methods({ obj[property] = value; Meteor.users.update({_id: id}, {$set: obj}); - } -}); \ No newline at end of file + }, + + //Inspired by: https://dweldon.silvrback.com/impersonating-a-user + impersonateUser: function(targetUserId) { + check(targetUserId, String); + + var user = Meteor.user(); + if (!user || !Roles.userIsInRole(user, ['admin'])) + throw new Meteor.Error(401, "You need to be an admin to impersonate a user."); + + if (! Meteor.users.findOne(targetUserId)) + throw new Meteor.Error(422, "Unable to find targetUserId to impersonate: " + targetUserId); + + this.setUserId(targetUserId); + + }, + +}); diff --git a/server/publish.js b/server/publish.js index 236205d..fb551b5 100644 --- a/server/publish.js +++ b/server/publish.js @@ -3,5 +3,6 @@ Meteor.publish('roles', function (){ }); Meteor.publish('filteredUsers', function(filter) { + check(filter, Match.OneOf(String, RegExp, null)); return filteredUserQuery(this.userId, filter); -}); \ No newline at end of file +}); diff --git a/server/startup.js b/server/startup.js index cdda152..f92c939 100644 --- a/server/startup.js +++ b/server/startup.js @@ -3,4 +3,4 @@ Meteor.startup(function() { if (Meteor.roles.find({name: 'admin'}).count() < 1 ) { Roles.createRole('admin'); } -}); \ No newline at end of file +}); From 273bec7a8975543ddb3633e060acf14b5311d03a Mon Sep 17 00:00:00 2001 From: andy lash Date: Fri, 16 May 2014 12:01:14 -0700 Subject: [PATCH 02/16] added configuration, last login support --- README.md | 72 +++++++++++++++++++++++++++-- client/accounts_admin.html | 13 +++++- client/accounts_admin.js | 10 +++- client/impersonate_account_modal.js | 6 ++- libs/user_query.js | 4 +- package.js | 4 ++ server/methods.js | 7 ++- 7 files changed, 107 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e8fe474..a916eb4 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ A roles based account management system using bootstrap 3 for Meteor. - [History](#history) - [Quick Start](#quick-start) - [Iron Router Integration](#iron-router-integration) +- [Configuration & Optional Features](#configuration) - [Contributing](#contributing) ## TODO @@ -105,7 +106,7 @@ if (Meteor.isClient) { @@ -35,4 +35,4 @@

Account Info

{{/with}} - \ No newline at end of file + diff --git a/client/info_account_modal.js b/client/info_account_modal.js index 0a1577b..ecf9237 100644 --- a/client/info_account_modal.js +++ b/client/info_account_modal.js @@ -22,19 +22,25 @@ Template.infoAccountModalInner.helpers({ return Session.get('userInScope'); }, - rolePairs: function() { - var pairs = []; - if (!this.roles) - pairs.push({key: 'Roles', value: 'None'}); - - for (var role in this.roles) { - var r = this.roles[role]; - if (role === '0') { - pairs.push({key: 'Roles', value: r}); - } else { - pairs.push({key: '-', value: r}); - } - } - return pairs; - } + roles: function() { + var self = this; + var roles = []; + if (_.isArray(self.roles)) { + roles = self.roles; + } else if (_.isObject(self.roles)) { + if (_.isArray(self.roles[Roles.GLOBAL_GROUP])) { + self.roles[Roles.GLOBAL_GROUP].forEach(function(role) { + roles.push(role); + }); + } + _.keys(self.roles).forEach(function(group) { + if (group !== Roles.GLOBAL_GROUP) { + self.roles[group].forEach(function(role) { + roles.push(role + ' (' + group + ')'); + }); + } + }); + } + return roles; + } }); diff --git a/client/startup.js b/client/startup.js index a417ef5..be28c32 100644 --- a/client/startup.js +++ b/client/startup.js @@ -1,10 +1,20 @@ +/* global AccountsAdmin */ +"use strict"; + +AccountsAdmin.subscribe = function() { + return [Meteor.subscribe('roles'), + Meteor.subscribe('filteredUsers', { + filter: Session.get('accountsAdminUserFilter') || '', + skip: Session.get("accountsAdminSkip") || null, + sort: Session.get("accountsAdminSortKey") || null, + })]; +}; Meteor.startup(function() { - if (!accountsAdminUiConfiguration || !accountsAdminUiConfiguration.manualSubscriptions) { - Meteor.subscribe('roles'); - Deps.autorun(function() { - Meteor.subscribe('filteredUsers', Session.get('userFilter')); - }); - } -}); \ No newline at end of file + if (!AccountsAdmin.config.manualSubscriptions) { + Tracker.autorun(function() { + AccountsAdmin.subscribe(); + }); + } +}); diff --git a/client/update_account_modal.html b/client/update_account_modal.html index b8436db..bad0b7a 100644 --- a/client/update_account_modal.html +++ b/client/update_account_modal.html @@ -20,7 +20,7 @@

Update {{email}}

- {{#if roles}} + + {{/if}}