From ecd067aab2115289a0d14272578995644f280120 Mon Sep 17 00:00:00 2001 From: Aditya Date: Thu, 30 May 2019 00:12:21 +0530 Subject: [PATCH 01/86] [NEW] Service Account Admin Settings and Configuration files added --- app/service-accounts/client/index.js | 5 + app/service-accounts/client/route.js | 13 +++ app/service-accounts/client/startup.js | 11 ++ .../client/views/serviceAccountDashboard.html | 88 +++++++++++++++ .../client/views/serviceAccountDashboard.js | 104 ++++++++++++++++++ app/service-accounts/server/config.js | 17 +++ app/service-accounts/server/index.js | 2 + app/service-accounts/server/permissions.js | 19 ++++ client/importPackages.js | 1 + server/importPackages.js | 1 + 10 files changed, 261 insertions(+) create mode 100644 app/service-accounts/client/index.js create mode 100644 app/service-accounts/client/route.js create mode 100644 app/service-accounts/client/startup.js create mode 100644 app/service-accounts/client/views/serviceAccountDashboard.html create mode 100644 app/service-accounts/client/views/serviceAccountDashboard.js create mode 100644 app/service-accounts/server/config.js create mode 100644 app/service-accounts/server/index.js create mode 100644 app/service-accounts/server/permissions.js diff --git a/app/service-accounts/client/index.js b/app/service-accounts/client/index.js new file mode 100644 index 000000000000..1960fbb22109 --- /dev/null +++ b/app/service-accounts/client/index.js @@ -0,0 +1,5 @@ +import './startup'; +import './route'; + +//views +import './views/serviceAccountDashboard'; \ No newline at end of file diff --git a/app/service-accounts/client/route.js b/app/service-accounts/client/route.js new file mode 100644 index 000000000000..1cf85e6b5920 --- /dev/null +++ b/app/service-accounts/client/route.js @@ -0,0 +1,13 @@ +import { FlowRouter } from 'meteor/kadira:flow-router'; +import { BlazeLayout } from 'meteor/kadira:blaze-layout'; +import { t } from '../../utils'; + +FlowRouter.route('/admin/serviceaccount', { + name: 'admin-serviceaccount', + action() { + return BlazeLayout.render('main', { + center: 'serviceAccountDashboard', + pageTitle: t('Service_account'), + }); + }, +}); \ No newline at end of file diff --git a/app/service-accounts/client/startup.js b/app/service-accounts/client/startup.js new file mode 100644 index 000000000000..c2bf795d85a3 --- /dev/null +++ b/app/service-accounts/client/startup.js @@ -0,0 +1,11 @@ +import { AdminBox } from '../../ui-utils'; +import { hasAtLeastOnePermission } from '../../authorization'; + +AdminBox.addOption({ + icon: 'discover', + href: 'admin/serviceaccount', + i18nLabel: 'Service_account_dashboard', + permissionGranted() { + return hasAtLeastOnePermission(['view-service-account-request']); + }, +}); \ No newline at end of file diff --git a/app/service-accounts/client/views/serviceAccountDashboard.html b/app/service-accounts/client/views/serviceAccountDashboard.html new file mode 100644 index 000000000000..c8cf9d5f747a --- /dev/null +++ b/app/service-accounts/client/views/serviceAccountDashboard.html @@ -0,0 +1,88 @@ + \ No newline at end of file diff --git a/app/service-accounts/client/views/serviceAccountDashboard.js b/app/service-accounts/client/views/serviceAccountDashboard.js new file mode 100644 index 000000000000..8a99dd5a3f7e --- /dev/null +++ b/app/service-accounts/client/views/serviceAccountDashboard.js @@ -0,0 +1,104 @@ +import { Meteor } from 'meteor/meteor'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Template } from 'meteor/templating'; +import { Tracker } from 'meteor/tracker'; +import { t } from '../../../utils/client'; +import _ from 'underscore'; + +import { handleError } from '../../../utils/client/lib/handleError'; +import { SideNav } from '../../../ui-utils'; +import { modal } from '../../../ui-utils/client/lib/modal'; +import { hasAllPermission } from '../../../authorization/client/hasPermission'; +import FullUser from '../../../models/client/models/FullUser'; +import './serviceAccountDashboard.html'; + +Template.serviceAccountDashboard.helpers({ + isReady() { + const instance = Template.instance(); + return instance.ready && instance.ready.get(); + }, + users() { + return Template.instance().users(); + }, + hasPermission() { + return hasAllPermission('view-service-account-request'); + }, + hasUsers() { + return Template.instance().users() && Template.instance().users().count() > 0; + }, + emailAddress() { + return _.map(this.emails, function(e) { return e.address; }).join(', '); + }, + hasMore() { + const instance = Template.instance(); + const users = instance.users(); + if (instance.limit && instance.limit.get() && users && users.length) { + return instance.limit.get() === users.length; + } + } +}); + +Template.serviceAccountDashboard.events({ + 'click .user-info'(e, instance) { + e.preventDefault(); + modal.open({ + title: t('Are_you_sure'), + text: t('The_user_s_will_be_allowed_to_create_service_accounts', this.username), + type: 'warning', + showCancelButton: true, + confirmButtonColor: '#DD6B55', + confirmButtonText: t('Yes'), + cancelButtonText: t('Cancel'), + closeOnConfirm: false, + html: false, + }, () => { + Meteor.call('authorization:removeUserFromRole', 'service-account-applied', this.username, null, function(error) { + if (error) { + return handleError(error); + } + }); + Meteor.call('authorization:addUserToRole', 'service-account-approved', this.username, null, function(error) { + if (error) { + return handleError(error); + } + + modal.open({ + title: t('Added'), + text: t('User_added'), + type: 'success', + timer: 1000, + showConfirmButton: false, + }); + }); + }); + }, +}); + +Template.serviceAccountDashboard.onCreated(function() { + const instance = this; + this.limit = new ReactiveVar(50); + this.ready = new ReactiveVar(true); + this.filter = new ReactiveVar(''); + + this.autorun(() => { + const filter = instance.filter.get(); + const limit = instance.limit.get(); + const subscription = instance.subscribe('fullUserData', filter, limit); + instance.ready.set(subscription.ready()); + }); + this.users = function() { + roles = [].concat('service-account-applied'); + const query = { + roles: { $in: roles }, + }; + const limit = instance.limit && instance.limit.get(); + return FullUser.find(query, { limit, sort: { username: 1, name: 1 } }).fetch(); + }; +}); + +Template.serviceAccountDashboard.onRendered(() => { + Tracker.afterFlush(() => { + SideNav.setFlex('adminFlex'); + SideNav.openFlex(); + }); +}); \ No newline at end of file diff --git a/app/service-accounts/server/config.js b/app/service-accounts/server/config.js new file mode 100644 index 000000000000..01dde2f951bd --- /dev/null +++ b/app/service-accounts/server/config.js @@ -0,0 +1,17 @@ +import { Meteor } from 'meteor/meteor'; +import { settings } from '../../settings'; + +Meteor.startup(() => { + settings.addGroup('Service Accounts', function() { + this.add('Service_account_enabled', true, { + group: 'Service Accounts', + i18nLabel: 'Enable', + type: 'boolean', + public: true, + }); + this.add('Service_account_limit', 3, { + type: 'int', + public: true, + }); + }); +}); diff --git a/app/service-accounts/server/index.js b/app/service-accounts/server/index.js new file mode 100644 index 000000000000..a4b3db92352a --- /dev/null +++ b/app/service-accounts/server/index.js @@ -0,0 +1,2 @@ +import './config'; +import './permissions'; diff --git a/app/service-accounts/server/permissions.js b/app/service-accounts/server/permissions.js new file mode 100644 index 000000000000..5f1a648f6956 --- /dev/null +++ b/app/service-accounts/server/permissions.js @@ -0,0 +1,19 @@ +import { Meteor } from 'meteor/meteor'; +import _ from 'underscore'; + +import { Roles, Permissions } from '../../models'; + +Meteor.startup(() => { + const roles = _.pluck(Roles.find().fetch(), 'name'); + if (roles.indexOf('service-account-applied') === -1) { + Roles.createOrUpdate('service-account-applied'); + } + if (roles.indexOf('service-account-approved') === -1) { + Roles.createOrUpdate('service-account-approved'); + } + if (Permissions) { + Permissions.createOrUpdate('view-sa-request', ['admin']); + Permissions.createOrUpdate('create-service-account', ['service-account-approved', 'admin']); + Permissions.createOrUpdate('delete-service-account', ['admin']); + } +}); diff --git a/client/importPackages.js b/client/importPackages.js index 32e492e5d21d..8f228f484e75 100644 --- a/client/importPackages.js +++ b/client/importPackages.js @@ -55,6 +55,7 @@ import '../app/otr/client'; import '../app/push-notifications/client'; import '../app/apps/client'; import '../app/setup-wizard/client'; +import '../app/service-accounts/client'; import '../app/slackbridge/client'; import '../app/slashcommands-archiveroom/client'; import '../app/slashcommand-asciiarts/client'; diff --git a/server/importPackages.js b/server/importPackages.js index 40daed01b3de..b1671a0d3bf8 100644 --- a/server/importPackages.js +++ b/server/importPackages.js @@ -67,6 +67,7 @@ import '../app/push-notifications/server'; import '../app/retention-policy'; import '../app/apps/server'; import '../app/setup-wizard/server'; +import '../app/service-accounts/server'; import '../app/slackbridge/server'; import '../app/slashcommands-archiveroom/server'; import '../app/slashcommand-asciiarts/server'; From b20c8c763f94c476e7e3c8dba4c0d15f144e0f75 Mon Sep 17 00:00:00 2001 From: Aditya Date: Thu, 30 May 2019 00:20:12 +0530 Subject: [PATCH 02/86] [NEW] Service Account Admin Settings and Configuration files added --- app/service-accounts/client/route.js | 2 +- packages/rocketchat-i18n/i18n/en.i18n.json | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/service-accounts/client/route.js b/app/service-accounts/client/route.js index 1cf85e6b5920..7fcb45d7a3a6 100644 --- a/app/service-accounts/client/route.js +++ b/app/service-accounts/client/route.js @@ -7,7 +7,7 @@ FlowRouter.route('/admin/serviceaccount', { action() { return BlazeLayout.render('main', { center: 'serviceAccountDashboard', - pageTitle: t('Service_account'), + pageTitle: t('Service_account_applied'), }); }, }); \ No newline at end of file diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index c965c1abc9e9..58f41949bb2c 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2628,6 +2628,9 @@ "Server_Type": "Server Type", "Service": "Service", "Service_account_key": "Service account key", + "Service_account_applied": "User applications for creating Service Accounts", + "Service_account_dashboard": "Service Account Dashboard", + "Service_account_limit": "Service Accounts per user", "set-leader": "Set Leader", "set-moderator": "Set Moderator", "set-moderator_description": "Permission to set other users as moderator of a channel", @@ -2817,6 +2820,7 @@ "The_server_will_restart_in_s_seconds": "The server will restart in %s seconds", "The_setting_s_is_configured_to_s_and_you_are_accessing_from_s": "The setting %s is configured to %s and you are accessing from %s!", "The_user_will_be_removed_from_s": "The user will be removed from %s", + "The_user_s_will_be_allowed_to_create_service_accounts": "This user %s will be allowed to create Service Accounts", "The_user_s_will_be_removed_from_role_s": "The user %s will be removed from role %s", "The_user_wont_be_able_to_type_in_s": "The user won't be able to type in %s", "Theme": "Theme", From 1c86e17642597ae1cadfece51686d6b2fcd07cc6 Mon Sep 17 00:00:00 2001 From: Aditya Date: Fri, 31 May 2019 00:44:36 +0530 Subject: [PATCH 03/86] [NEW] Service Account Admin Settings and Configuration files added --- app/service-accounts/client/index.js | 4 +- app/service-accounts/client/route.js | 3 +- app/service-accounts/client/startup.js | 2 +- .../client/views/serviceAccountDashboard.js | 72 +++++++++---------- app/service-accounts/server/config.js | 11 +-- app/service-accounts/server/permissions.js | 6 +- 6 files changed, 50 insertions(+), 48 deletions(-) diff --git a/app/service-accounts/client/index.js b/app/service-accounts/client/index.js index 1960fbb22109..4e3620a65970 100644 --- a/app/service-accounts/client/index.js +++ b/app/service-accounts/client/index.js @@ -1,5 +1,5 @@ import './startup'; import './route'; -//views -import './views/serviceAccountDashboard'; \ No newline at end of file +// views +import './views/serviceAccountDashboard'; diff --git a/app/service-accounts/client/route.js b/app/service-accounts/client/route.js index 7fcb45d7a3a6..427712b18604 100644 --- a/app/service-accounts/client/route.js +++ b/app/service-accounts/client/route.js @@ -1,5 +1,6 @@ import { FlowRouter } from 'meteor/kadira:flow-router'; import { BlazeLayout } from 'meteor/kadira:blaze-layout'; + import { t } from '../../utils'; FlowRouter.route('/admin/serviceaccount', { @@ -10,4 +11,4 @@ FlowRouter.route('/admin/serviceaccount', { pageTitle: t('Service_account_applied'), }); }, -}); \ No newline at end of file +}); diff --git a/app/service-accounts/client/startup.js b/app/service-accounts/client/startup.js index c2bf795d85a3..e7a722cd5984 100644 --- a/app/service-accounts/client/startup.js +++ b/app/service-accounts/client/startup.js @@ -8,4 +8,4 @@ AdminBox.addOption({ permissionGranted() { return hasAtLeastOnePermission(['view-service-account-request']); }, -}); \ No newline at end of file +}); diff --git a/app/service-accounts/client/views/serviceAccountDashboard.js b/app/service-accounts/client/views/serviceAccountDashboard.js index 8a99dd5a3f7e..17fff95db404 100644 --- a/app/service-accounts/client/views/serviceAccountDashboard.js +++ b/app/service-accounts/client/views/serviceAccountDashboard.js @@ -2,9 +2,9 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; import { Tracker } from 'meteor/tracker'; -import { t } from '../../../utils/client'; import _ from 'underscore'; +import { t } from '../../../utils/client'; import { handleError } from '../../../utils/client/lib/handleError'; import { SideNav } from '../../../ui-utils'; import { modal } from '../../../ui-utils/client/lib/modal'; @@ -17,16 +17,16 @@ Template.serviceAccountDashboard.helpers({ const instance = Template.instance(); return instance.ready && instance.ready.get(); }, - users() { + users() { return Template.instance().users(); - }, - hasPermission() { + }, + hasPermission() { return hasAllPermission('view-service-account-request'); - }, - hasUsers() { + }, + hasUsers() { return Template.instance().users() && Template.instance().users().count() > 0; - }, - emailAddress() { + }, + emailAddress() { return _.map(this.emails, function(e) { return e.address; }).join(', '); }, hasMore() { @@ -35,22 +35,22 @@ Template.serviceAccountDashboard.helpers({ if (instance.limit && instance.limit.get() && users && users.length) { return instance.limit.get() === users.length; } - } + }, }); Template.serviceAccountDashboard.events({ - 'click .user-info'(e, instance) { - e.preventDefault(); - modal.open({ - title: t('Are_you_sure'), - text: t('The_user_s_will_be_allowed_to_create_service_accounts', this.username), - type: 'warning', - showCancelButton: true, - confirmButtonColor: '#DD6B55', - confirmButtonText: t('Yes'), - cancelButtonText: t('Cancel'), - closeOnConfirm: false, - html: false, + 'click .user-info'(e) { + e.preventDefault(); + modal.open({ + title: t('Are_you_sure'), + text: t('The_user_s_will_be_allowed_to_create_service_accounts', this.username), + type: 'warning', + showCancelButton: true, + confirmButtonColor: '#DD6B55', + confirmButtonText: t('Yes'), + cancelButtonText: t('Cancel'), + closeOnConfirm: false, + html: false, }, () => { Meteor.call('authorization:removeUserFromRole', 'service-account-applied', this.username, null, function(error) { if (error) { @@ -75,20 +75,20 @@ Template.serviceAccountDashboard.events({ }); Template.serviceAccountDashboard.onCreated(function() { - const instance = this; + const instance = this; this.limit = new ReactiveVar(50); - this.ready = new ReactiveVar(true); - this.filter = new ReactiveVar(''); - - this.autorun(() => { - const filter = instance.filter.get(); - const limit = instance.limit.get(); - const subscription = instance.subscribe('fullUserData', filter, limit); + this.ready = new ReactiveVar(true); + this.filter = new ReactiveVar(''); + + this.autorun(() => { + const filter = instance.filter.get(); + const limit = instance.limit.get(); + const subscription = instance.subscribe('fullUserData', filter, limit); instance.ready.set(subscription.ready()); - }); - this.users = function() { - roles = [].concat('service-account-applied'); - const query = { + }); + this.users = function() { + const roles = [].concat('service-account-applied'); + const query = { roles: { $in: roles }, }; const limit = instance.limit && instance.limit.get(); @@ -97,8 +97,8 @@ Template.serviceAccountDashboard.onCreated(function() { }); Template.serviceAccountDashboard.onRendered(() => { - Tracker.afterFlush(() => { + Tracker.afterFlush(() => { SideNav.setFlex('adminFlex'); SideNav.openFlex(); - }); -}); \ No newline at end of file + }); +}); diff --git a/app/service-accounts/server/config.js b/app/service-accounts/server/config.js index 01dde2f951bd..3f7c7b16d360 100644 --- a/app/service-accounts/server/config.js +++ b/app/service-accounts/server/config.js @@ -1,4 +1,5 @@ import { Meteor } from 'meteor/meteor'; + import { settings } from '../../settings'; Meteor.startup(() => { @@ -8,10 +9,10 @@ Meteor.startup(() => { i18nLabel: 'Enable', type: 'boolean', public: true, - }); - this.add('Service_account_limit', 3, { - type: 'int', - public: true, - }); + }); + this.add('Service_account_limit', 3, { + type: 'int', + public: true, + }); }); }); diff --git a/app/service-accounts/server/permissions.js b/app/service-accounts/server/permissions.js index 5f1a648f6956..d72e6303aae3 100644 --- a/app/service-accounts/server/permissions.js +++ b/app/service-accounts/server/permissions.js @@ -12,8 +12,8 @@ Meteor.startup(() => { Roles.createOrUpdate('service-account-approved'); } if (Permissions) { - Permissions.createOrUpdate('view-sa-request', ['admin']); - Permissions.createOrUpdate('create-service-account', ['service-account-approved', 'admin']); - Permissions.createOrUpdate('delete-service-account', ['admin']); + Permissions.createOrUpdate('view-sa-request', ['admin']); + Permissions.createOrUpdate('create-service-account', ['service-account-approved', 'admin']); + Permissions.createOrUpdate('delete-service-account', ['admin']); } }); From a4eb68289e0bc9d0da1509b2724764132b44277b Mon Sep 17 00:00:00 2001 From: Aditya Date: Fri, 31 May 2019 01:04:26 +0530 Subject: [PATCH 04/86] [NEW] Service Account Admin Settings and Configuration files added --- app/service-accounts/client/views/serviceAccountDashboard.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/service-accounts/client/views/serviceAccountDashboard.js b/app/service-accounts/client/views/serviceAccountDashboard.js index 17fff95db404..8be0a19db785 100644 --- a/app/service-accounts/client/views/serviceAccountDashboard.js +++ b/app/service-accounts/client/views/serviceAccountDashboard.js @@ -87,8 +87,8 @@ Template.serviceAccountDashboard.onCreated(function() { instance.ready.set(subscription.ready()); }); this.users = function() { - const roles = [].concat('service-account-applied'); - const query = { + const roles = [].concat('service-account-applied'); + const query = { roles: { $in: roles }, }; const limit = instance.limit && instance.limit.get(); From a7359a4b5c152ff859167ccf5243a94e5145f491 Mon Sep 17 00:00:00 2001 From: Aditya Date: Fri, 31 May 2019 23:58:53 +0530 Subject: [PATCH 05/86] Service Account Creation dialog added --- app/service-accounts/client/index.js | 1 + .../creationDialog/createServiceAccount.html | 56 +++++++++++++ .../creationDialog/createServiceAccount.js | 23 ++++++ app/ui-sidenav/client/sidebarHeader.js | 81 ++++++++++++------- 4 files changed, 132 insertions(+), 29 deletions(-) create mode 100644 app/service-accounts/client/views/creationDialog/createServiceAccount.html create mode 100644 app/service-accounts/client/views/creationDialog/createServiceAccount.js diff --git a/app/service-accounts/client/index.js b/app/service-accounts/client/index.js index 4e3620a65970..1c1add36d12a 100644 --- a/app/service-accounts/client/index.js +++ b/app/service-accounts/client/index.js @@ -3,3 +3,4 @@ import './route'; // views import './views/serviceAccountDashboard'; +import './views/creationDialog/createServiceAccount'; \ No newline at end of file diff --git a/app/service-accounts/client/views/creationDialog/createServiceAccount.html b/app/service-accounts/client/views/creationDialog/createServiceAccount.html new file mode 100644 index 000000000000..6895c1b44b7f --- /dev/null +++ b/app/service-accounts/client/views/creationDialog/createServiceAccount.html @@ -0,0 +1,56 @@ + \ No newline at end of file diff --git a/app/service-accounts/client/views/creationDialog/createServiceAccount.js b/app/service-accounts/client/views/creationDialog/createServiceAccount.js new file mode 100644 index 000000000000..6ff2fb2e6c94 --- /dev/null +++ b/app/service-accounts/client/views/creationDialog/createServiceAccount.js @@ -0,0 +1,23 @@ +import { Meteor } from 'meteor/meteor'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Tracker } from 'meteor/tracker'; +import { Blaze } from 'meteor/blaze'; +import { FlowRouter } from 'meteor/kadira:flow-router'; +import { Template } from 'meteor/templating'; +import { AutoComplete } from 'meteor/mizzao:autocomplete'; +import toastr from 'toastr'; +import _ from 'underscore'; + +import { settings } from '../../../../settings'; +import { callbacks } from '../../../../callbacks'; +import { t, roomTypes } from '../../../../utils'; +import { hasAllPermission } from '../../../../authorization'; +import './createServiceAccount.html'; + +Template.createServiceAccount.helpers({ + +}); + +Template.createServiceAccount.events({ + +}); \ No newline at end of file diff --git a/app/ui-sidenav/client/sidebarHeader.js b/app/ui-sidenav/client/sidebarHeader.js index a35519ed0c62..9da7d3f47115 100644 --- a/app/ui-sidenav/client/sidebarHeader.js +++ b/app/ui-sidenav/client/sidebarHeader.js @@ -172,41 +172,64 @@ const toolbarButtons = (user) => [{ }; const discussionEnabled = settings.get('Discussion_enabled'); - if (!discussionEnabled) { - return createChannel(e); + const serviceAccountEnabled = settings.get('Service_account_enabled'); + const items = [{ + icon: 'hashtag', + name: t('Channel'), + action: createChannel, + }]; + if (discussionEnabled) { + items.push({ + icon: 'discussion', + name: t('Discussion'), + action: (e) => { + e.preventDefault(); + modal.open({ + title: t('Discussion_title'), + content: 'CreateDiscussion', + data: { + onCreate() { + modal.close(); + }, + }, + modifier: 'modal', + showConfirmButton: false, + showCancelButton: false, + confirmOnEnter: false, + }); + }, + }); + } + + if (serviceAccountEnabled) { + items.push({ + icon: 'user', + name: t('Service_account'), + action: (e) => { + e.preventDefault(); + modal.open({ + title: t('Service_account_title'), + content: 'createServiceAccount', + data: { + onCreate() { + modal.close(); + }, + }, + modifier: 'modal', + showConfirmButton: false, + showCancelButton: false, + confirmOnEnter: false, + }); + }, + }); } + const config = { columns: [ { groups: [ { - items: [ - { - icon: 'hashtag', - name: t('Channel'), - action: createChannel, - }, - { - icon: 'discussion', - name: t('Discussion'), - action: (e) => { - e.preventDefault(); - modal.open({ - title: t('Discussion_title'), - content: 'CreateDiscussion', - data: { - onCreate() { - modal.close(); - }, - }, - modifier: 'modal', - showConfirmButton: false, - showCancelButton: false, - confirmOnEnter: false, - }); - }, - }, - ], + items, }, ], }, From de8ec3cfb24abd75760fc931bda293b4ed36230e Mon Sep 17 00:00:00 2001 From: Aditya Date: Mon, 3 Jun 2019 01:15:38 +0530 Subject: [PATCH 06/86] [NEW] Service Account Creation method --- app/lib/server/functions/saveUser.js | 10 +- .../creationDialog/createServiceAccount.html | 46 +++++-- .../creationDialog/createServiceAccount.js | 115 ++++++++++++++++-- app/service-accounts/server/index.js | 4 + .../server/methods/addServiceAccount.js | 45 +++++++ .../server/methods/usernameExists.js | 18 +++ app/ui-sidenav/client/sidebarHeader.js | 4 +- packages/rocketchat-i18n/i18n/en.i18n.json | 5 + 8 files changed, 227 insertions(+), 20 deletions(-) create mode 100644 app/service-accounts/server/methods/addServiceAccount.js create mode 100644 app/service-accounts/server/methods/usernameExists.js diff --git a/app/lib/server/functions/saveUser.js b/app/lib/server/functions/saveUser.js index 1d478416ee0b..4f680792ffea 100644 --- a/app/lib/server/functions/saveUser.js +++ b/app/lib/server/functions/saveUser.js @@ -159,7 +159,9 @@ export const saveUser = function(userId, userData) { validateUserData(userId, userData); if (!userData._id) { - validateEmailDomain(userData.email); + if (userData.email) { + validateEmailDomain(userData.email); + } // insert user const createUser = { @@ -192,6 +194,12 @@ export const saveUser = function(userId, userData) { updateUser.$set['emails.0.verified'] = userData.verified; } + if (typeof userData.u !== 'undefined') { + updateUser.$set.u = userData.u; + updateUser.$set.description = userData.description; + updateUser.$set.active = true; + } + Meteor.users.update({ _id }, updateUser); if (userData.sendWelcomeEmail) { diff --git a/app/service-accounts/client/views/creationDialog/createServiceAccount.html b/app/service-accounts/client/views/creationDialog/createServiceAccount.html index 6895c1b44b7f..2f19c929f1cc 100644 --- a/app/service-accounts/client/views/creationDialog/createServiceAccount.html +++ b/app/service-accounts/client/views/creationDialog/createServiceAccount.html @@ -6,13 +6,13 @@
{{#if inUse}} @@ -20,17 +20,28 @@
{{> icon block="rc-input__error-icon" icon="warning" classes="rc-input__error-icon-svg"}}
-
{{_ "Channel_already_exist_static"}}
+
{{_ "Username_already_exist"}}
{{/if}}
+ +
+
+ {{#if notMatch}} +
+
+ {{> icon block="rc-input__error-icon" icon="warning" classes="rc-input__error-icon-svg"}} +
+
{{_ "Invalid_confirm_pass"}}
+
+ {{/if}} +
+
+
- +
diff --git a/app/service-accounts/client/views/creationDialog/createServiceAccount.js b/app/service-accounts/client/views/creationDialog/createServiceAccount.js index 6ff2fb2e6c94..adef0bbcabf4 100644 --- a/app/service-accounts/client/views/creationDialog/createServiceAccount.js +++ b/app/service-accounts/client/views/creationDialog/createServiceAccount.js @@ -1,23 +1,120 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; -import { Blaze } from 'meteor/blaze'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { Template } from 'meteor/templating'; -import { AutoComplete } from 'meteor/mizzao:autocomplete'; import toastr from 'toastr'; import _ from 'underscore'; -import { settings } from '../../../../settings'; -import { callbacks } from '../../../../callbacks'; -import { t, roomTypes } from '../../../../utils'; -import { hasAllPermission } from '../../../../authorization'; +import { t, handleError } from '../../../../utils'; +import { modal } from '../../../../ui-utils'; + import './createServiceAccount.html'; Template.createServiceAccount.helpers({ + inUse() { + return Template.instance().inUse.get(); + }, + notMatch() { + return Template.instance().notMatch.get(); + }, + createIsDisabled() { + const instance = Template.instance(); + const username = instance.username.get(); + const name = instance.name.get(); + const password = instance.password.get(); + const description = instance.description.get(); + const inUse = instance.inUse.get(); + const notMatch = instance.notMatch.get(); + const confirmPassword = instance.confirmPassword.get(); + if (username.length === 0 || name.length ===0 || password.length === 0 || confirmPassword.length === 0 || description.length === 0 || inUse || notMatch) { + return 'disabled'; + } + return ''; + }, }); Template.createServiceAccount.events({ + 'input [name="username"]'(e, t) { + const { value } = e.target; + t.inUse.set(undefined); + t.checkUsername(value.trim()); + t.username.set(value.trim()); + }, + 'input [name="name"]'(e, t) { + const { value } = e.target; + t.name.set(value); + }, + 'input [name="password"]'(e, t) { + const { value } = e.target; + t.password.set(value); + }, + 'input [name="confirmPassword"]'(e, t) { + const { value } = e.target; + t.confirmPassword.set(value); + t.matchPassword(t.password.get(), value); + }, + 'mouseover [name="password"]'(e) { + e.target.type = 'text'; + }, + 'mouseover [name="confirmPassword"]'(e) { + e.target.type = 'text'; + }, + 'mouseout [name="password"]'(e) { + e.target.type = 'password'; + }, + 'mouseout [name="confirmPassword"]'(e) { + e.target.type = 'password'; + }, + 'input [name="description"]'(e, t) { + const { value } = e.target; + t.description.set(value); + }, + async 'submit #create-service-account'(event, instance) { + event.preventDefault(); + event.stopPropagation(); + const username = instance.username.get(); + const name = instance.name.get(); + const password = instance.password.get(); + const description = instance.description.get(); + const userData = { + username: username, + name: name, + password: password, + description: description, + }; + Meteor.call('addServiceAccount', userData, (error) => { + if (error) { + return handleError(error); + } + toastr.success(t('Service_account_created_successfully')); + modal.close(); + }); + }, +}); + +Template.createServiceAccount.onCreated(function () { + this.username = new ReactiveVar(''); + this.name = new ReactiveVar(''); + this.password = new ReactiveVar(''); + this.confirmPassword = new ReactiveVar(''); + this.description = new ReactiveVar(''); + this.inUse = new ReactiveVar(undefined); + this.notMatch = new ReactiveVar(false); + this.checkUsername = _.debounce((name) => { + return Meteor.call('usernameExists', name, (error, result) => { + if (error) { + return; + } + this.inUse.set(result); + }); + }, 1000); + this.matchPassword = function (password, confirmPassword) { + if (password != confirmPassword) { + this.notMatch.set(true); + } + else { + this.notMatch.set(false); + } + } +}); -}); \ No newline at end of file diff --git a/app/service-accounts/server/index.js b/app/service-accounts/server/index.js index a4b3db92352a..1be56895b7fe 100644 --- a/app/service-accounts/server/index.js +++ b/app/service-accounts/server/index.js @@ -1,2 +1,6 @@ import './config'; import './permissions'; + +// methods +import './methods/usernameExists'; +import './methods/addServiceAccount'; \ No newline at end of file diff --git a/app/service-accounts/server/methods/addServiceAccount.js b/app/service-accounts/server/methods/addServiceAccount.js new file mode 100644 index 000000000000..ca5d6392b2e4 --- /dev/null +++ b/app/service-accounts/server/methods/addServiceAccount.js @@ -0,0 +1,45 @@ +import { Meteor } from 'meteor/meteor'; +import { check } from 'meteor/check'; + +import { saveUser, checkUsernameAvailability } from '../../../lib/server/functions'; +import { settings } from '../../../settings'; +import { hasPermission } from '../../../authorization/server'; + +Meteor.methods({ + addServiceAccount(userData) { + check(userData, Object); + + if (!Meteor.userId()) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'addServiceAccount' }); + } + + if (!hasPermission(Meteor.userId(), 'create-service-account')) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'addServiceAccount' }); + } + + let nameValidation; + try { + nameValidation = new RegExp(`^${ settings.get('UTF8_Names_Validation') }$`); + } catch (error) { + nameValidation = new RegExp('^[0-9a-zA-Z-_.]+$'); + } + + if (!nameValidation.test(userData.username)) { + throw new Meteor.Error('username-invalid', `${ _.escape(username) } is not a valid username, use only letters, numbers, dots, hyphens and underscores`); + } + + if (!checkUsernameAvailability(userData.username)) { + throw new Meteor.Error('error-field-unavailable', `${ _.escape(username) } is already in use :(`, { method: 'addServiceAccount' }); + } + + const user = Meteor.user(); + userData.u = { + _id: user._id, + username: user.username, + }; + userData.joinDefaultChannels = false; + userData.roles = ['user']; + + return saveUser(Meteor.userId(), userData); + }, +}); diff --git a/app/service-accounts/server/methods/usernameExists.js b/app/service-accounts/server/methods/usernameExists.js new file mode 100644 index 000000000000..1a74d8bcceb3 --- /dev/null +++ b/app/service-accounts/server/methods/usernameExists.js @@ -0,0 +1,18 @@ +import { Meteor } from 'meteor/meteor'; +import { check } from 'meteor/check'; + +import { Users } from '../../../models'; + +Meteor.methods({ + usernameExists(name) { + check(name, String); + + if (!Meteor.userId()) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { + method: 'roomExists', + }); + } + const user = Users.findOneByUsername(name, {}); + return !!user; + } +}); \ No newline at end of file diff --git a/app/ui-sidenav/client/sidebarHeader.js b/app/ui-sidenav/client/sidebarHeader.js index 9da7d3f47115..a862d197d683 100644 --- a/app/ui-sidenav/client/sidebarHeader.js +++ b/app/ui-sidenav/client/sidebarHeader.js @@ -7,7 +7,7 @@ import { popover, AccountBox, menu, SideNav, modal } from '../../ui-utils'; import { t, getUserPreference, handleError } from '../../utils'; import { callbacks } from '../../callbacks'; import { settings } from '../../settings'; -import { hasAtLeastOnePermission } from '../../authorization'; +import { hasAtLeastOnePermission, hasPermission } from '../../authorization'; const setStatus = (status) => { AccountBox.setStatus(status); @@ -201,7 +201,7 @@ const toolbarButtons = (user) => [{ }); } - if (serviceAccountEnabled) { + if (serviceAccountEnabled && hasPermission('create-service-account')) { items.push({ icon: 'user', name: t('Service_account'), diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index a34e6799e920..4599dc842d66 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2629,10 +2629,15 @@ "Server_Info": "Server Info", "Server_Type": "Server Type", "Service": "Service", + "Service_account": "Service Account", "Service_account_key": "Service account key", "Service_account_applied": "User applications for creating Service Accounts", + "Service_account_created_successfully": "Service Account created successfully", "Service_account_dashboard": "Service Account Dashboard", "Service_account_limit": "Service Accounts per user", + "Service_account_name_placeholder": "Service Account name", + "Service_account_username_placeholder": "Service Account username", + "Service_account_title": "Create a new Service Account", "set-leader": "Set Leader", "set-moderator": "Set Moderator", "set-moderator_description": "Permission to set other users as moderator of a channel", From 285ec8311644710ecee8301af61b90320b3f0e77 Mon Sep 17 00:00:00 2001 From: Aditya Date: Mon, 3 Jun 2019 23:38:14 +0530 Subject: [PATCH 07/86] Service Account owner username update method added --- app/lib/server/functions/setUsername.js | 1 + app/models/server/models/Users.js | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/app/lib/server/functions/setUsername.js b/app/lib/server/functions/setUsername.js index baed79351cf8..0c4424fc3690 100644 --- a/app/lib/server/functions/setUsername.js +++ b/app/lib/server/functions/setUsername.js @@ -79,6 +79,7 @@ export const _setUsername = function(userId, u) { Subscriptions.setUserUsernameByUserId(user._id, username); Subscriptions.setNameForDirectRoomsWithOldName(previousUsername, username); LivechatDepartmentAgents.replaceUsernameOfAgentByUserId(user._id, username); + Users.setOwnerUsernameByUserId(user._id, username); const fileStore = FileUpload.getStore('Avatars'); const file = fileStore.model.findOneByName(previousUsername); diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js index 0daea240a2ab..75cd1008110e 100644 --- a/app/models/server/models/Users.js +++ b/app/models/server/models/Users.js @@ -1046,6 +1046,17 @@ export class Users extends Base { }); } + setOwnerUsernameByUserId(userId, username) { + const query = { 'u._id': userId }; + const update = { + $set: { + 'u.username': username, + }, + }; + + return this.update(query, update, { multi: true }); + } + // INSERT create(data) { const user = { From 4e2477ccab1dfc889e68efa1905d9fb7c56ef728 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 4 Jun 2019 01:38:41 +0530 Subject: [PATCH 08/86] Fixed CLI errors --- app/service-accounts/client/index.js | 2 +- .../creationDialog/createServiceAccount.js | 163 +++++++++--------- app/service-accounts/server/index.js | 2 +- .../server/methods/addServiceAccount.js | 31 ++-- .../server/methods/usernameExists.js | 14 +- 5 files changed, 105 insertions(+), 107 deletions(-) diff --git a/app/service-accounts/client/index.js b/app/service-accounts/client/index.js index 1c1add36d12a..011b64062925 100644 --- a/app/service-accounts/client/index.js +++ b/app/service-accounts/client/index.js @@ -3,4 +3,4 @@ import './route'; // views import './views/serviceAccountDashboard'; -import './views/creationDialog/createServiceAccount'; \ No newline at end of file +import './views/creationDialog/createServiceAccount'; diff --git a/app/service-accounts/client/views/creationDialog/createServiceAccount.js b/app/service-accounts/client/views/creationDialog/createServiceAccount.js index adef0bbcabf4..ca05b1e41f58 100644 --- a/app/service-accounts/client/views/creationDialog/createServiceAccount.js +++ b/app/service-accounts/client/views/creationDialog/createServiceAccount.js @@ -10,23 +10,23 @@ import { modal } from '../../../../ui-utils'; import './createServiceAccount.html'; Template.createServiceAccount.helpers({ - inUse() { + inUse() { return Template.instance().inUse.get(); - }, - notMatch() { + }, + notMatch() { return Template.instance().notMatch.get(); - }, - createIsDisabled() { + }, + createIsDisabled() { const instance = Template.instance(); - const username = instance.username.get(); - const name = instance.name.get(); - const password = instance.password.get(); - const description = instance.description.get(); - const inUse = instance.inUse.get(); - const notMatch = instance.notMatch.get(); - const confirmPassword = instance.confirmPassword.get(); + const username = instance.username.get(); + const name = instance.name.get(); + const password = instance.password.get(); + const description = instance.description.get(); + const inUse = instance.inUse.get(); + const notMatch = instance.notMatch.get(); + const confirmPassword = instance.confirmPassword.get(); - if (username.length === 0 || name.length ===0 || password.length === 0 || confirmPassword.length === 0 || description.length === 0 || inUse || notMatch) { + if (username.length === 0 || name.length === 0 || password.length === 0 || confirmPassword.length === 0 || description.length === 0 || inUse || notMatch) { return 'disabled'; } return ''; @@ -34,87 +34,84 @@ Template.createServiceAccount.helpers({ }); Template.createServiceAccount.events({ - 'input [name="username"]'(e, t) { - const { value } = e.target; - t.inUse.set(undefined); - t.checkUsername(value.trim()); - t.username.set(value.trim()); - }, - 'input [name="name"]'(e, t) { - const { value } = e.target; - t.name.set(value); - }, - 'input [name="password"]'(e, t) { - const { value } = e.target; - t.password.set(value); - }, - 'input [name="confirmPassword"]'(e, t) { - const { value } = e.target; - t.confirmPassword.set(value); - t.matchPassword(t.password.get(), value); - }, - 'mouseover [name="password"]'(e) { - e.target.type = 'text'; - }, - 'mouseover [name="confirmPassword"]'(e) { - e.target.type = 'text'; - }, - 'mouseout [name="password"]'(e) { - e.target.type = 'password'; - }, - 'mouseout [name="confirmPassword"]'(e) { - e.target.type = 'password'; - }, - 'input [name="description"]'(e, t) { - const { value } = e.target; - t.description.set(value); - }, - async 'submit #create-service-account'(event, instance) { - event.preventDefault(); - event.stopPropagation(); - const username = instance.username.get(); - const name = instance.name.get(); - const password = instance.password.get(); - const description = instance.description.get(); - const userData = { - username: username, - name: name, - password: password, - description: description, - }; - Meteor.call('addServiceAccount', userData, (error) => { + 'input [name="username"]'(e, t) { + const { value } = e.target; + t.inUse.set(undefined); + t.checkUsername(value.trim()); + t.username.set(value.trim()); + }, + 'input [name="name"]'(e, t) { + const { value } = e.target; + t.name.set(value); + }, + 'input [name="password"]'(e, t) { + const { value } = e.target; + t.password.set(value); + }, + 'input [name="confirmPassword"]'(e, t) { + const { value } = e.target; + t.confirmPassword.set(value); + t.matchPassword(t.password.get(), value); + }, + 'mouseover [name="password"]'(e) { + e.target.type = 'text'; + }, + 'mouseover [name="confirmPassword"]'(e) { + e.target.type = 'text'; + }, + 'mouseout [name="password"]'(e) { + e.target.type = 'password'; + }, + 'mouseout [name="confirmPassword"]'(e) { + e.target.type = 'password'; + }, + 'input [name="description"]'(e, t) { + const { value } = e.target; + t.description.set(value); + }, + async 'submit #create-service-account'(event, instance) { + event.preventDefault(); + event.stopPropagation(); + const username = instance.username.get(); + const name = instance.name.get(); + const password = instance.password.get(); + const description = instance.description.get(); + const userData = {}; + userData.username = username; + userData.name = name; + userData.password = password; + userData.description = description; + Meteor.call('addServiceAccount', userData, (error) => { if (error) { return handleError(error); } - toastr.success(t('Service_account_created_successfully')); - modal.close(); + toastr.success(t('Service_account_created_successfully')); + modal.close(); }); - }, + }, }); Template.createServiceAccount.onCreated(function () { - this.username = new ReactiveVar(''); - this.name = new ReactiveVar(''); - this.password = new ReactiveVar(''); - this.confirmPassword = new ReactiveVar(''); - this.description = new ReactiveVar(''); - this.inUse = new ReactiveVar(undefined); - this.notMatch = new ReactiveVar(false); - this.checkUsername = _.debounce((name) => { + this.username = new ReactiveVar(''); + this.name = new ReactiveVar(''); + this.password = new ReactiveVar(''); + this.confirmPassword = new ReactiveVar(''); + this.description = new ReactiveVar(''); + this.inUse = new ReactiveVar(undefined); + this.notMatch = new ReactiveVar(false); + this.checkUsername = _.debounce(function(name) { return Meteor.call('usernameExists', name, (error, result) => { if (error) { return; } this.inUse.set(result); }); - }, 1000); - this.matchPassword = function (password, confirmPassword) { - if (password != confirmPassword) { - this.notMatch.set(true); - } - else { - this.notMatch.set(false); - } - } + }, 1000); + this.matchPassword = function (password, confirmPassword) { + if (password !== confirmPassword) { + this.notMatch.set(true); + } else { + this.notMatch.set(false); + } + }; }); - diff --git a/app/service-accounts/server/index.js b/app/service-accounts/server/index.js index 1be56895b7fe..dedeaff52cb5 100644 --- a/app/service-accounts/server/index.js +++ b/app/service-accounts/server/index.js @@ -3,4 +3,4 @@ import './permissions'; // methods import './methods/usernameExists'; -import './methods/addServiceAccount'; \ No newline at end of file +import './methods/addServiceAccount'; diff --git a/app/service-accounts/server/methods/addServiceAccount.js b/app/service-accounts/server/methods/addServiceAccount.js index ca5d6392b2e4..ca1976f6b44a 100644 --- a/app/service-accounts/server/methods/addServiceAccount.js +++ b/app/service-accounts/server/methods/addServiceAccount.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; +import _ from 'underscore'; import { saveUser, checkUsernameAvailability } from '../../../lib/server/functions'; import { settings } from '../../../settings'; @@ -11,13 +12,13 @@ Meteor.methods({ if (!Meteor.userId()) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'addServiceAccount' }); - } + } - if (!hasPermission(Meteor.userId(), 'create-service-account')) { - throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'addServiceAccount' }); - } + if (!hasPermission(Meteor.userId(), 'create-service-account')) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'addServiceAccount' }); + } - let nameValidation; + let nameValidation; try { nameValidation = new RegExp(`^${ settings.get('UTF8_Names_Validation') }$`); } catch (error) { @@ -25,20 +26,20 @@ Meteor.methods({ } if (!nameValidation.test(userData.username)) { - throw new Meteor.Error('username-invalid', `${ _.escape(username) } is not a valid username, use only letters, numbers, dots, hyphens and underscores`); + throw new Meteor.Error('username-invalid', `${ _.escape(userData.username) } is not a valid username, use only letters, numbers, dots, hyphens and underscores`); } if (!checkUsernameAvailability(userData.username)) { - throw new Meteor.Error('error-field-unavailable', `${ _.escape(username) } is already in use :(`, { method: 'addServiceAccount' }); - } + throw new Meteor.Error('error-field-unavailable', `${ _.escape(userData.username) } is already in use :(`, { method: 'addServiceAccount' }); + } - const user = Meteor.user(); - userData.u = { - _id: user._id, - username: user.username, - }; - userData.joinDefaultChannels = false; - userData.roles = ['user']; + const user = Meteor.user(); + userData.u = { + _id: user._id, + username: user.username, + }; + userData.joinDefaultChannels = false; + userData.roles = ['user']; return saveUser(Meteor.userId(), userData); }, diff --git a/app/service-accounts/server/methods/usernameExists.js b/app/service-accounts/server/methods/usernameExists.js index 1a74d8bcceb3..82dfc7f58445 100644 --- a/app/service-accounts/server/methods/usernameExists.js +++ b/app/service-accounts/server/methods/usernameExists.js @@ -4,15 +4,15 @@ import { check } from 'meteor/check'; import { Users } from '../../../models'; Meteor.methods({ - usernameExists(name) { - check(name, String); + usernameExists(name) { + check(name, String); if (!Meteor.userId()) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'roomExists', }); - } - const user = Users.findOneByUsername(name, {}); - return !!user; - } -}); \ No newline at end of file + } + const user = Users.findOneByUsername(name, {}); + return !!user; + }, +}); From e5e51ddc83265def9e4227ecd61da6b048e0df3e Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 4 Jun 2019 01:44:37 +0530 Subject: [PATCH 09/86] Fixed CLI errors --- .../client/views/creationDialog/createServiceAccount.js | 6 +++--- app/service-accounts/server/methods/addServiceAccount.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/service-accounts/client/views/creationDialog/createServiceAccount.js b/app/service-accounts/client/views/creationDialog/createServiceAccount.js index ca05b1e41f58..9384605c9683 100644 --- a/app/service-accounts/client/views/creationDialog/createServiceAccount.js +++ b/app/service-accounts/client/views/creationDialog/createServiceAccount.js @@ -14,7 +14,7 @@ Template.createServiceAccount.helpers({ return Template.instance().inUse.get(); }, notMatch() { - return Template.instance().notMatch.get(); + return Template.instance().notMatch.get(); }, createIsDisabled() { const instance = Template.instance(); @@ -91,7 +91,7 @@ Template.createServiceAccount.events({ }, }); -Template.createServiceAccount.onCreated(function () { +Template.createServiceAccount.onCreated(function() { this.username = new ReactiveVar(''); this.name = new ReactiveVar(''); this.password = new ReactiveVar(''); @@ -107,7 +107,7 @@ Template.createServiceAccount.onCreated(function () { this.inUse.set(result); }); }, 1000); - this.matchPassword = function (password, confirmPassword) { + this.matchPassword = function(password, confirmPassword) { if (password !== confirmPassword) { this.notMatch.set(true); } else { diff --git a/app/service-accounts/server/methods/addServiceAccount.js b/app/service-accounts/server/methods/addServiceAccount.js index ca1976f6b44a..edd45b40bf44 100644 --- a/app/service-accounts/server/methods/addServiceAccount.js +++ b/app/service-accounts/server/methods/addServiceAccount.js @@ -32,7 +32,7 @@ Meteor.methods({ if (!checkUsernameAvailability(userData.username)) { throw new Meteor.Error('error-field-unavailable', `${ _.escape(userData.username) } is already in use :(`, { method: 'addServiceAccount' }); } - + const user = Meteor.user(); userData.u = { _id: user._id, From a68a1d39a0de26d72b46e689f1854471b66ddec1 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 4 Jun 2019 23:47:10 +0530 Subject: [PATCH 10/86] Service Account creation heading fixed --- .../client/views/creationDialog/createServiceAccount.html | 2 +- packages/rocketchat-i18n/i18n/en.i18n.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/service-accounts/client/views/creationDialog/createServiceAccount.html b/app/service-accounts/client/views/creationDialog/createServiceAccount.html index 2f19c929f1cc..3d8b968cb879 100644 --- a/app/service-accounts/client/views/creationDialog/createServiceAccount.html +++ b/app/service-accounts/client/views/creationDialog/createServiceAccount.html @@ -1,7 +1,7 @@ diff --git a/app/ui/client/views/app/directory.js b/app/ui/client/views/app/directory.js index ba4af117e7f0..6936335c66fe 100644 --- a/app/ui/client/views/app/directory.js +++ b/app/ui/client/views/app/directory.js @@ -36,6 +36,17 @@ function directorySearch(config, cb) { domain: result.federation && result.federation.peer, }; } + + if (config.type === 'serviceAccounts') { + return { + name: result.name, + username: result.username, + createdAt: timeAgo(result.createdAt, t), + description: result.description, + subscribers: result.subscribersCount || 0, + domain: result.federation && result.federation.peer, + } + } return null; })); }); @@ -96,13 +107,20 @@ Template.directory.helpers({ return true; }, }; + const serviceAccountsTab = { + label: t('Service_accounts'), + value: 'serviceAccounts', + condition() { + return true; + }, + }; if (searchType.get() === 'channels') { channelsTab.active = true; } else { usersTab.active = true; } return { - tabs: [channelsTab, usersTab], + tabs: [channelsTab, usersTab, serviceAccountsTab], onChange(value) { results.set([]); end.set(false); diff --git a/server/methods/browseChannels.js b/server/methods/browseChannels.js index 2d8d9ffb3c7f..9369405bc449 100644 --- a/server/methods/browseChannels.js +++ b/server/methods/browseChannels.js @@ -28,11 +28,20 @@ const sortUsers = function(field, direction) { } }; +const sortServiceAccounts = function(field, direction) { + switch (field) { + default: + return { + [field]: direction === 'asc' ? 1 : -1, + }; + } +}; + Meteor.methods({ browseChannels({ text = '', workspace = '', type = 'channels', sortBy = 'name', sortDirection = 'asc', page, offset, limit = 10 }) { const regex = new RegExp(s.trim(s.escapeRegExp(text)), 'i'); - if (!['channels', 'users'].includes(type)) { + if (!['channels', 'users', 'serviceAccounts'].includes(type)) { return; } @@ -85,6 +94,40 @@ Meteor.methods({ }; } + if (type === 'serviceAccounts') { + const options = { + ...pagination, + sort: sortServiceAccounts(sortBy, sortDirection), + fields: { + username: 1, + name: 1, + createdAt: 1, + description: 1, + subscribers: 1, + federation: 1, + }, + }; + + const exceptions = [user.username]; + const forcedSearchFields = workspace === 'all' && ['username', 'name', 'description']; + + let result; + if (workspace === 'all') { + result = Users.findByActiveServiceAccountsExcept(text, exceptions, forcedSearchFields, options); + } else if (workspace === 'external') { + result = Users.findByActiveExternalServiceAccountsExcept(text, exceptions, options, forcedSearchFields, Federation.localIdentifier); + } else { + result = Users.findByActiveLocalServiceAccountsExcept(text, exceptions, options, forcedSearchFields, Federation.localIdentifier); + } + const total = result.count(); + const results = result.fetch(); + + return { + total, + results, + }; + } + // type === users if (!hasPermission(user._id, 'view-outside-room') || !hasPermission(user._id, 'view-d-room')) { return; From b5871ed646d31d585065be8f6152957106db52ae Mon Sep 17 00:00:00 2001 From: Aditya Date: Sun, 16 Jun 2019 00:15:56 +0530 Subject: [PATCH 19/86] Refactored creation method and added tests --- app/lib/server/functions/saveUser.js | 1 + app/models/server/models/Users.js | 6 ++ .../client/views/serviceAccountDashboard.html | 44 +++++++------ .../client/views/serviceAccountDashboard.js | 64 ++++++++----------- app/service-accounts/server/api/rest.js | 1 + .../server/api/v1/serviceAccounts.js | 24 +++++++ app/service-accounts/server/index.js | 3 + .../server/methods/addServiceAccount.js | 10 ++- app/service-accounts/server/permissions.js | 6 +- .../publications/fullServiceAccountData.js | 34 ++++++++++ packages/rocketchat-i18n/i18n/en.i18n.json | 3 +- server/lib/accounts.js | 4 ++ tests/end-to-end/api/01-users.js | 32 ++++++++++ 13 files changed, 168 insertions(+), 64 deletions(-) create mode 100644 app/service-accounts/server/api/rest.js create mode 100644 app/service-accounts/server/api/v1/serviceAccounts.js create mode 100644 app/service-accounts/server/publications/fullServiceAccountData.js diff --git a/app/lib/server/functions/saveUser.js b/app/lib/server/functions/saveUser.js index 1f7afdeb283d..9e83754f92d9 100644 --- a/app/lib/server/functions/saveUser.js +++ b/app/lib/server/functions/saveUser.js @@ -174,6 +174,7 @@ export const saveUser = function(userId, userData) { } if (userData.u) { createUser.u = userData.u; + createUser.active = userData.active; } const _id = Accounts.createUser(createUser); diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js index 75cd1008110e..b9374ea84b5d 100644 --- a/app/models/server/models/Users.js +++ b/app/models/server/models/Users.js @@ -665,6 +665,12 @@ export class Users extends Base { return this.findOne(query, options); } + findLinkedServiceAccounts(_id, options) { + const query = { 'u._id': _id }; + + return this.find(query, options); + } + // UPDATE addImportIds(_id, importIds) { importIds = [].concat(importIds); diff --git a/app/service-accounts/client/views/serviceAccountDashboard.html b/app/service-accounts/client/views/serviceAccountDashboard.html index c8cf9d5f747a..ae968a254501 100644 --- a/app/service-accounts/client/views/serviceAccountDashboard.html +++ b/app/service-accounts/client/views/serviceAccountDashboard.html @@ -9,27 +9,32 @@
{{{_ "Showing_results" users.length}}}
- {{#table fixed='true' onItemClick=onTableItemClick onScroll=onTableScroll onResize=onTableResize}} + {{#table fixed='true' onScroll=onTableScroll onResize=onTableResize}} - +
{{_ "Name"}}
- +
{{_ "Username"}}
- -
{{_ "Email"}}
+ +
{{_ "Description"}}
- -
{{_ "Roles"}}
+ +
{{_ "Approve"}}
- -
{{_ "Status"}}
+ +
{{_ "Reject"}}
+ {{#unless hasUsers}} + + {{_ "There_are_no_service_accounts_to_approve"}} + + {{/unless}} {{#each users}} @@ -52,26 +57,26 @@
- {{emailAddress}} + {{description}}
- {{roles}} +
- -
{{#if not active}}{{_"deactivated"}}{{else}}{{status}}{{/if}}
+ +
+
+ +
+
- {{else}} {{# with searchText}} - - {{_ "No_results_found_for"}} {{.}} - - {{/with}} {{/each}} {{#unless isReady}} + {{/each}} {{#unless isReady}} {{> loading}} @@ -81,8 +86,5 @@ {{/unless}} - {{#with flexData}} - {{> flexTabBar}} - {{/with}} \ No newline at end of file diff --git a/app/service-accounts/client/views/serviceAccountDashboard.js b/app/service-accounts/client/views/serviceAccountDashboard.js index 8be0a19db785..45671a2a6f9a 100644 --- a/app/service-accounts/client/views/serviceAccountDashboard.js +++ b/app/service-accounts/client/views/serviceAccountDashboard.js @@ -1,17 +1,26 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; +import toastr from 'toastr'; import { Tracker } from 'meteor/tracker'; import _ from 'underscore'; import { t } from '../../../utils/client'; import { handleError } from '../../../utils/client/lib/handleError'; import { SideNav } from '../../../ui-utils'; -import { modal } from '../../../ui-utils/client/lib/modal'; import { hasAllPermission } from '../../../authorization/client/hasPermission'; import FullUser from '../../../models/client/models/FullUser'; import './serviceAccountDashboard.html'; +const success = (fn) => function(error, result) { + if (error) { + return handleError(error); + } + if (result) { + fn.call(this, result); + } +}; + Template.serviceAccountDashboard.helpers({ isReady() { const instance = Template.instance(); @@ -21,10 +30,10 @@ Template.serviceAccountDashboard.helpers({ return Template.instance().users(); }, hasPermission() { - return hasAllPermission('view-service-account-request'); + return hasAllPermission('view-sa-request'); }, hasUsers() { - return Template.instance().users() && Template.instance().users().count() > 0; + return Template.instance().users() && Template.instance().users().length > 0; }, emailAddress() { return _.map(this.emails, function(e) { return e.address; }).join(', '); @@ -39,38 +48,17 @@ Template.serviceAccountDashboard.helpers({ }); Template.serviceAccountDashboard.events({ - 'click .user-info'(e) { + 'click .accept-service-account'(e) { e.preventDefault(); - modal.open({ - title: t('Are_you_sure'), - text: t('The_user_s_will_be_allowed_to_create_service_accounts', this.username), - type: 'warning', - showCancelButton: true, - confirmButtonColor: '#DD6B55', - confirmButtonText: t('Yes'), - cancelButtonText: t('Cancel'), - closeOnConfirm: false, - html: false, - }, () => { - Meteor.call('authorization:removeUserFromRole', 'service-account-applied', this.username, null, function(error) { - if (error) { - return handleError(error); - } - }); - Meteor.call('authorization:addUserToRole', 'service-account-approved', this.username, null, function(error) { - if (error) { - return handleError(error); - } - - modal.open({ - title: t('Added'), - text: t('User_added'), - type: 'success', - timer: 1000, - showConfirmButton: false, - }); - }); - }); + Meteor.call('authorization:addUserToRole', 'service-account-approved', this.u.username, null, success(() => { + Meteor.call('setUserActiveStatus', this._id, true, success(() => toastr.success(t('User_has_been_activated')))); + })); + }, + 'click .reject-service-account'(e) { + e.preventDefault(); + Meteor.call('deleteUser', this._id, success(() => { + toastr.success(t('User_has_been_deleted')); + })); }, }); @@ -83,13 +71,15 @@ Template.serviceAccountDashboard.onCreated(function() { this.autorun(() => { const filter = instance.filter.get(); const limit = instance.limit.get(); - const subscription = instance.subscribe('fullUserData', filter, limit); + const subscription = instance.subscribe('fullServiceAccountData', filter, limit); instance.ready.set(subscription.ready()); }); this.users = function() { - const roles = [].concat('service-account-applied'); const query = { - roles: { $in: roles }, + u: { + $exists: true, + }, + active: false, }; const limit = instance.limit && instance.limit.get(); return FullUser.find(query, { limit, sort: { username: 1, name: 1 } }).fetch(); diff --git a/app/service-accounts/server/api/rest.js b/app/service-accounts/server/api/rest.js new file mode 100644 index 000000000000..6a6beb89e4a8 --- /dev/null +++ b/app/service-accounts/server/api/rest.js @@ -0,0 +1 @@ +import './v1/serviceAccounts'; diff --git a/app/service-accounts/server/api/v1/serviceAccounts.js b/app/service-accounts/server/api/v1/serviceAccounts.js new file mode 100644 index 000000000000..0541120e8f26 --- /dev/null +++ b/app/service-accounts/server/api/v1/serviceAccounts.js @@ -0,0 +1,24 @@ +import { Meteor } from 'meteor/meteor'; +import { check } from 'meteor/check'; + +import { API } from '../../../../api'; +import { Users } from '../../../../models'; + +API.v1.addRoute('serviceAccounts.create', { authRequired: true }, { + post() { + check(this.bodyParams, { + name: String, + password: String, + username: String, + description: String, + }); + Meteor.runAsUser(this.userId, () => { + Meteor.call('addServiceAccount', this.bodyParams, function (err, id) { + if (err) { + console.log(err); + } + }); + }); + return API.v1.success({ user: Users.findOneByUsername(this.bodyParams.username, { fields: API.v1.defaultFieldsToExclude }) }); + }, +}); diff --git a/app/service-accounts/server/index.js b/app/service-accounts/server/index.js index dedeaff52cb5..71a5dcb7f1af 100644 --- a/app/service-accounts/server/index.js +++ b/app/service-accounts/server/index.js @@ -1,6 +1,9 @@ import './config'; import './permissions'; +import './api/rest'; // methods import './methods/usernameExists'; import './methods/addServiceAccount'; + +import './publications/fullServiceAccountData'; diff --git a/app/service-accounts/server/methods/addServiceAccount.js b/app/service-accounts/server/methods/addServiceAccount.js index edd45b40bf44..fd5fef28a847 100644 --- a/app/service-accounts/server/methods/addServiceAccount.js +++ b/app/service-accounts/server/methods/addServiceAccount.js @@ -3,8 +3,9 @@ import { check } from 'meteor/check'; import _ from 'underscore'; import { saveUser, checkUsernameAvailability } from '../../../lib/server/functions'; +import { Users } from '../../../models'; import { settings } from '../../../settings'; -import { hasPermission } from '../../../authorization/server'; +import { hasPermission, hasRole } from '../../../authorization/server'; Meteor.methods({ addServiceAccount(userData) { @@ -34,6 +35,12 @@ Meteor.methods({ } const user = Meteor.user(); + const serviceAccounts = Users.findLinkedServiceAccounts(user._id, {}); + + if (serviceAccounts.count() >= 3) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'addServiceAccount' }); + } + userData.u = { _id: user._id, username: user.username, @@ -41,6 +48,7 @@ Meteor.methods({ userData.joinDefaultChannels = false; userData.roles = ['user']; + userData.active = hasRole(user._id, 'service-account-approved'); return saveUser(Meteor.userId(), userData); }, }); diff --git a/app/service-accounts/server/permissions.js b/app/service-accounts/server/permissions.js index d72e6303aae3..7a24e94afe03 100644 --- a/app/service-accounts/server/permissions.js +++ b/app/service-accounts/server/permissions.js @@ -5,15 +5,13 @@ import { Roles, Permissions } from '../../models'; Meteor.startup(() => { const roles = _.pluck(Roles.find().fetch(), 'name'); - if (roles.indexOf('service-account-applied') === -1) { - Roles.createOrUpdate('service-account-applied'); - } + if (roles.indexOf('service-account-approved') === -1) { Roles.createOrUpdate('service-account-approved'); } if (Permissions) { Permissions.createOrUpdate('view-sa-request', ['admin']); - Permissions.createOrUpdate('create-service-account', ['service-account-approved', 'admin']); + Permissions.createOrUpdate('create-service-account', ['user', 'admin']); Permissions.createOrUpdate('delete-service-account', ['admin']); } }); diff --git a/app/service-accounts/server/publications/fullServiceAccountData.js b/app/service-accounts/server/publications/fullServiceAccountData.js new file mode 100644 index 000000000000..4c962d4233fe --- /dev/null +++ b/app/service-accounts/server/publications/fullServiceAccountData.js @@ -0,0 +1,34 @@ +import { Meteor } from 'meteor/meteor'; + +import { Users } from '../../../models'; + +Meteor.publish('fullServiceAccountData', function (filter, limit) { + if (!this.userId) { + return this.ready(); + } + + const query = { + u: { + $exists: true, + }, + active: false, + }; + + const handle = Users.find(query, {}).observeChanges({ + added: (id, fields) => { + this.added('rocketchat_full_user', id, fields); + }, + + changed: (id, fields) => { + this.changed('rocketchat_full_user', id, fields); + }, + + removed: (id) => { + this.removed('rocketchat_full_user', id); + }, + }); + + this.ready(); + this.onStop(() => handle.stop()); + +}); \ No newline at end of file diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 003da9f7a982..e1850a486f59 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2634,7 +2634,7 @@ "Service": "Service", "Service_account": "Service Account", "Service_account_key": "Service account key", - "Service_account_applied": "User applications for creating Service Accounts", + "Service_account_applied": "Service Accounts approval applications", "Service_account_created_successfully": "Service Account created successfully", "Service_account_dashboard": "Service Account Dashboard", "Service_account_description": "Service Accounts are an upgrade to existing user accounts. You can connect to a large number of users using service account with exclusive features such as broadcast message to all your subscribers at once", @@ -2892,6 +2892,7 @@ "There_are_no_applications_installed": "There are currently no Rocket.Chat Applications installed.", "There_are_no_integrations": "There are no integrations", "There_are_no_personal_access_tokens_created_yet": "There are no Personal Access Tokens created yet.", + "There_are_no_service_accounts_to_approve": "There are no service accounts to approve", "There_are_no_users_in_this_role": "There are no users in this role.", "This_conversation_is_already_closed": "This conversation is already closed.", "This_email_has_already_been_used_and_has_not_been_verified__Please_change_your_password": "This email has already been used and has not been verified. Please change your password.", diff --git a/server/lib/accounts.js b/server/lib/accounts.js index 2dafa50eeafe..722be3c91287 100644 --- a/server/lib/accounts.js +++ b/server/lib/accounts.js @@ -102,6 +102,10 @@ Accounts.onCreateUser(function(options, user = {}) { user.status = 'offline'; user.active = !settings.get('Accounts_ManuallyApproveNewUsers'); + if (options.active !== undefined) { + user.active = options.active; + } + if (!user.name) { if (options.profile) { if (options.profile.name) { diff --git a/tests/end-to-end/api/01-users.js b/tests/end-to-end/api/01-users.js index c34069ff33cb..e554331634b0 100644 --- a/tests/end-to-end/api/01-users.js +++ b/tests/end-to-end/api/01-users.js @@ -1653,4 +1653,36 @@ describe('[Users]', function() { }); }); }); + describe('[Service Accounts]', () => { + it('should create a new service account', (done) => { + + const username = `serviceAccount_${apiUsername}`; + const description = 'Test Service Account'; + + request.post(api('serviceAccounts.create')) + .set(credentials) + .send({ + name: username, + username: username, + password, + description: description, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('user.username', username); + expect(res.body).to.have.nested.property('user.u.username'); + expect(res.body).to.have.nested.property('user.active', false); + expect(res.body).to.have.nested.property('user.name', username); + expect(res.body).to.have.nested.property('user.description', description); + expect(res.body).to.not.have.nested.property('user.e2e'); + expect(res.body).to.not.have.nested.property('user.customFields'); + + targetUser._id = res.body.user._id; + targetUser.username = res.body.user.username; + }) + .end(done); + }); + }); }); From 574268f3d0661cb381d0b774d3533a2794be26b7 Mon Sep 17 00:00:00 2001 From: Aditya Date: Sun, 16 Jun 2019 00:29:42 +0530 Subject: [PATCH 20/86] CLI errors fixed --- app/models/server/models/Users.js | 2 +- .../client/views/serviceAccountDashboard.js | 9 ++------- app/service-accounts/server/api/v1/serviceAccounts.js | 6 +----- .../server/publications/fullServiceAccountData.js | 5 ++--- tests/end-to-end/api/01-users.js | 5 ++--- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js index b9374ea84b5d..0ef83f31fcbc 100644 --- a/app/models/server/models/Users.js +++ b/app/models/server/models/Users.js @@ -667,7 +667,7 @@ export class Users extends Base { findLinkedServiceAccounts(_id, options) { const query = { 'u._id': _id }; - + return this.find(query, options); } diff --git a/app/service-accounts/client/views/serviceAccountDashboard.js b/app/service-accounts/client/views/serviceAccountDashboard.js index 45671a2a6f9a..aaaffc1483d7 100644 --- a/app/service-accounts/client/views/serviceAccountDashboard.js +++ b/app/service-accounts/client/views/serviceAccountDashboard.js @@ -64,14 +64,10 @@ Template.serviceAccountDashboard.events({ Template.serviceAccountDashboard.onCreated(function() { const instance = this; - this.limit = new ReactiveVar(50); this.ready = new ReactiveVar(true); - this.filter = new ReactiveVar(''); this.autorun(() => { - const filter = instance.filter.get(); - const limit = instance.limit.get(); - const subscription = instance.subscribe('fullServiceAccountData', filter, limit); + const subscription = instance.subscribe('fullServiceAccountData'); instance.ready.set(subscription.ready()); }); this.users = function() { @@ -81,8 +77,7 @@ Template.serviceAccountDashboard.onCreated(function() { }, active: false, }; - const limit = instance.limit && instance.limit.get(); - return FullUser.find(query, { limit, sort: { username: 1, name: 1 } }).fetch(); + return FullUser.find(query, { sort: { username: 1, name: 1 } }).fetch(); }; }); diff --git a/app/service-accounts/server/api/v1/serviceAccounts.js b/app/service-accounts/server/api/v1/serviceAccounts.js index 0541120e8f26..e7dc3bc0ee2c 100644 --- a/app/service-accounts/server/api/v1/serviceAccounts.js +++ b/app/service-accounts/server/api/v1/serviceAccounts.js @@ -13,11 +13,7 @@ API.v1.addRoute('serviceAccounts.create', { authRequired: true }, { description: String, }); Meteor.runAsUser(this.userId, () => { - Meteor.call('addServiceAccount', this.bodyParams, function (err, id) { - if (err) { - console.log(err); - } - }); + Meteor.call('addServiceAccount', this.bodyParams); }); return API.v1.success({ user: Users.findOneByUsername(this.bodyParams.username, { fields: API.v1.defaultFieldsToExclude }) }); }, diff --git a/app/service-accounts/server/publications/fullServiceAccountData.js b/app/service-accounts/server/publications/fullServiceAccountData.js index 4c962d4233fe..53816ff4ad6c 100644 --- a/app/service-accounts/server/publications/fullServiceAccountData.js +++ b/app/service-accounts/server/publications/fullServiceAccountData.js @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { Users } from '../../../models'; -Meteor.publish('fullServiceAccountData', function (filter, limit) { +Meteor.publish('fullServiceAccountData', function() { if (!this.userId) { return this.ready(); } @@ -30,5 +30,4 @@ Meteor.publish('fullServiceAccountData', function (filter, limit) { this.ready(); this.onStop(() => handle.stop()); - -}); \ No newline at end of file +}); diff --git a/tests/end-to-end/api/01-users.js b/tests/end-to-end/api/01-users.js index e554331634b0..d2e5a66ff870 100644 --- a/tests/end-to-end/api/01-users.js +++ b/tests/end-to-end/api/01-users.js @@ -1655,8 +1655,7 @@ describe('[Users]', function() { }); describe('[Service Accounts]', () => { it('should create a new service account', (done) => { - - const username = `serviceAccount_${apiUsername}`; + const username = `serviceAccount_${ apiUsername }`; const description = 'Test Service Account'; request.post(api('serviceAccounts.create')) @@ -1664,8 +1663,8 @@ describe('[Users]', function() { .send({ name: username, username: username, - password, description: description, + password, }) .expect('Content-Type', 'application/json') .expect(200) From 93d7b4bb84adc72e546a5ffb5443068463eb1193 Mon Sep 17 00:00:00 2001 From: Aditya Date: Sun, 16 Jun 2019 00:38:48 +0530 Subject: [PATCH 21/86] CLI errors fixed --- tests/end-to-end/api/01-users.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/end-to-end/api/01-users.js b/tests/end-to-end/api/01-users.js index d2e5a66ff870..b63ad391ff47 100644 --- a/tests/end-to-end/api/01-users.js +++ b/tests/end-to-end/api/01-users.js @@ -1662,8 +1662,8 @@ describe('[Users]', function() { .set(credentials) .send({ name: username, - username: username, - description: description, + username, + description, password, }) .expect('Content-Type', 'application/json') From 6111b49bbb13765e81d33a72a34bb7d7107e3b29 Mon Sep 17 00:00:00 2001 From: Aditya Date: Mon, 17 Jun 2019 11:33:32 +0530 Subject: [PATCH 22/86] Bugs fixed --- app/service-accounts/client/startup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/service-accounts/client/startup.js b/app/service-accounts/client/startup.js index e7a722cd5984..410b0d452ec1 100644 --- a/app/service-accounts/client/startup.js +++ b/app/service-accounts/client/startup.js @@ -6,6 +6,6 @@ AdminBox.addOption({ href: 'admin/serviceaccount', i18nLabel: 'Service_account_dashboard', permissionGranted() { - return hasAtLeastOnePermission(['view-service-account-request']); + return hasAtLeastOnePermission(['view-sa-request']); }, }); From ea7398a2ba6481d2f66c2899bf4ebd85e79490b0 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 18 Jun 2019 00:58:30 +0530 Subject: [PATCH 23/86] [NEW] Service Accounts Login method --- .../views/serviceAccountSidebarLogin.html | 20 +++++++++++-------- .../views/serviceAccountSidebarLogin.js | 6 ++++-- app/service-accounts/server/index.js | 6 +----- .../server/methods/addServiceAccount.js | 5 +++-- .../publications/fullServiceAccountData.js | 2 +- packages/rocketchat-i18n/i18n/en.i18n.json | 2 ++ 6 files changed, 23 insertions(+), 18 deletions(-) diff --git a/app/service-accounts/client/views/serviceAccountSidebarLogin.html b/app/service-accounts/client/views/serviceAccountSidebarLogin.html index 7c79898c2c44..399539276223 100644 --- a/app/service-accounts/client/views/serviceAccountSidebarLogin.html +++ b/app/service-accounts/client/views/serviceAccountSidebarLogin.html @@ -10,14 +10,18 @@ {{else}} - {{#each users}} - - {{/each}} + {{#if hasServiceAccounts}} + {{#each users}} + + {{/each}} + {{else}} +

{{_ "You_have_no_service_accounts"}}

+ {{/if}} {{/if}} diff --git a/app/service-accounts/client/views/serviceAccountSidebarLogin.js b/app/service-accounts/client/views/serviceAccountSidebarLogin.js index 12ebe6316363..ff65e20ae456 100644 --- a/app/service-accounts/client/views/serviceAccountSidebarLogin.js +++ b/app/service-accounts/client/views/serviceAccountSidebarLogin.js @@ -14,6 +14,9 @@ Template.serviceAccountSidebarLogin.helpers({ users() { return Template.instance().users(); }, + hasServiceAccounts() { + return Template.instance().users() && Template.instance().users().length > 0; + }, owner() { return Meteor.user().u; }, @@ -34,13 +37,12 @@ Template.serviceAccountSidebarLogin.events({ if (error) { return handleError(error); } - popover.close(); + FlowRouter.go('/home'); Meteor.loginWithToken(token.token, (err) => { if (err) { console.log(err); } document.location.reload(true); - FlowRouter.go('/home'); if (Meteor.user().u) { localStorage.setItem('serviceAccountForceLogin', true); } diff --git a/app/service-accounts/server/index.js b/app/service-accounts/server/index.js index aa97634ca27a..f6a6fb8418af 100644 --- a/app/service-accounts/server/index.js +++ b/app/service-accounts/server/index.js @@ -5,13 +5,9 @@ import './api/rest'; // methods import './methods/usernameExists'; import './methods/addServiceAccount'; -<<<<<<< HEAD import './methods/getLoginToken'; import './hooks/serviceAccountCallback'; -import './publications/userServiceAccounts'; -======= - import './publications/fullServiceAccountData'; ->>>>>>> 6111b49bbb13765e81d33a72a34bb7d7107e3b29 +import './publications/userServiceAccounts'; diff --git a/app/service-accounts/server/methods/addServiceAccount.js b/app/service-accounts/server/methods/addServiceAccount.js index fd5fef28a847..596bf9e8d312 100644 --- a/app/service-accounts/server/methods/addServiceAccount.js +++ b/app/service-accounts/server/methods/addServiceAccount.js @@ -36,9 +36,10 @@ Meteor.methods({ const user = Meteor.user(); const serviceAccounts = Users.findLinkedServiceAccounts(user._id, {}); + const limit = settings.get('Service_account_limit'); - if (serviceAccounts.count() >= 3) { - throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'addServiceAccount' }); + if (serviceAccounts.count() >= limit) { + throw new Meteor.Error('error-not-allowed', 'Max service account limit reached', { method: 'addServiceAccount' }); } userData.u = { diff --git a/app/service-accounts/server/publications/fullServiceAccountData.js b/app/service-accounts/server/publications/fullServiceAccountData.js index 53816ff4ad6c..6a2035e51ce8 100644 --- a/app/service-accounts/server/publications/fullServiceAccountData.js +++ b/app/service-accounts/server/publications/fullServiceAccountData.js @@ -13,7 +13,7 @@ Meteor.publish('fullServiceAccountData', function() { }, active: false, }; - + const handle = Users.find(query, {}).observeChanges({ added: (id, fields) => { this.added('rocketchat_full_user', id, fields); diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index e1850a486f59..42082e156e3c 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2639,6 +2639,7 @@ "Service_account_dashboard": "Service Account Dashboard", "Service_account_description": "Service Accounts are an upgrade to existing user accounts. You can connect to a large number of users using service account with exclusive features such as broadcast message to all your subscribers at once", "Service_account_limit": "Service Accounts per user", + "Service_account_login": "Service Account login", "Service_account_name_placeholder": "Service Account name", "Service_account_username_placeholder": "Service Account username", "Service_account_title": "Create a new Service Account", @@ -3247,6 +3248,7 @@ "You_cant_leave_a_livechat_room_Please_use_the_close_button": "You can't leave a livechat room. Please, use the close button.", "You_have_been_muted": "You have been muted and cannot speak in this room", "You_have_n_codes_remaining": "You have __number__ codes remaining.", + "You_have_no_service_accounts": "You have no linked service accounts", "You_have_not_verified_your_email": "You have not verified your email.", "You_have_successfully_unsubscribed": "You have successfully unsubscribed from our Mailling List.", "You_have_to_set_an_API_token_first_in_order_to_use_the_integration": "You have to set an API token first in order to use the integration.", From 8a02753cf5cd93c4111d11bc31c91c0f06397704 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 18 Jun 2019 01:03:47 +0530 Subject: [PATCH 24/86] Typo fixed --- app/service-accounts/client/index.js | 2 +- .../client/views/serviceAccountSidebarLogin.html | 2 +- app/service-accounts/server/methods/getLoginToken.js | 2 +- .../server/publications/fullServiceAccountData.js | 2 +- app/service-accounts/server/publications/userServiceAccounts.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/service-accounts/client/index.js b/app/service-accounts/client/index.js index b5ff714806ac..ba096451948b 100644 --- a/app/service-accounts/client/index.js +++ b/app/service-accounts/client/index.js @@ -4,4 +4,4 @@ import './route'; // views import './views/serviceAccountDashboard'; import './views/creationDialog/createServiceAccount'; -import './views/serviceAccountSidebarLogin'; \ No newline at end of file +import './views/serviceAccountSidebarLogin'; diff --git a/app/service-accounts/client/views/serviceAccountSidebarLogin.html b/app/service-accounts/client/views/serviceAccountSidebarLogin.html index 399539276223..4fc8970a3fe3 100644 --- a/app/service-accounts/client/views/serviceAccountSidebarLogin.html +++ b/app/service-accounts/client/views/serviceAccountSidebarLogin.html @@ -25,4 +25,4 @@ {{/if}} - \ No newline at end of file + diff --git a/app/service-accounts/server/methods/getLoginToken.js b/app/service-accounts/server/methods/getLoginToken.js index 1d5e9b72fc61..8a2246b24fed 100644 --- a/app/service-accounts/server/methods/getLoginToken.js +++ b/app/service-accounts/server/methods/getLoginToken.js @@ -26,4 +26,4 @@ Meteor.methods({ Accounts._insertLoginToken(user._id, stampedToken); return stampedToken; }, -}); \ No newline at end of file +}); diff --git a/app/service-accounts/server/publications/fullServiceAccountData.js b/app/service-accounts/server/publications/fullServiceAccountData.js index 6a2035e51ce8..53816ff4ad6c 100644 --- a/app/service-accounts/server/publications/fullServiceAccountData.js +++ b/app/service-accounts/server/publications/fullServiceAccountData.js @@ -13,7 +13,7 @@ Meteor.publish('fullServiceAccountData', function() { }, active: false, }; - + const handle = Users.find(query, {}).observeChanges({ added: (id, fields) => { this.added('rocketchat_full_user', id, fields); diff --git a/app/service-accounts/server/publications/userServiceAccounts.js b/app/service-accounts/server/publications/userServiceAccounts.js index 756e721326cd..320617c866f6 100644 --- a/app/service-accounts/server/publications/userServiceAccounts.js +++ b/app/service-accounts/server/publications/userServiceAccounts.js @@ -22,4 +22,4 @@ Meteor.publish('userServiceAccounts', function () { this.ready(); this.onStop(() => handle.stop()); -}); \ No newline at end of file +}); From b8ab554c33795d88de0ae10c98ebefce86b7ead7 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 18 Jun 2019 01:22:35 +0530 Subject: [PATCH 25/86] CLI errors fixed --- .../client/views/serviceAccountSidebarLogin.js | 17 ++++++----------- .../server/methods/getLoginToken.js | 4 ++-- app/ui-sidenav/client/sidebarHeader.js | 2 +- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/app/service-accounts/client/views/serviceAccountSidebarLogin.js b/app/service-accounts/client/views/serviceAccountSidebarLogin.js index ff65e20ae456..9a34f73f7008 100644 --- a/app/service-accounts/client/views/serviceAccountSidebarLogin.js +++ b/app/service-accounts/client/views/serviceAccountSidebarLogin.js @@ -1,8 +1,9 @@ +import { Meteor } from 'meteor/meteor'; +import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { handleError } from '../../../utils'; -import { popover } from '../../../ui-utils'; import FullUser from '../../../models/client/models/FullUser'; import './serviceAccountSidebarLogin.html'; @@ -22,18 +23,18 @@ Template.serviceAccountSidebarLogin.helpers({ }, showOwnerAccountLink() { return localStorage.getItem('serviceAccountForceLogin') && !!Meteor.user().u; - } + }, }); Template.serviceAccountSidebarLogin.events({ 'click .js-login'(e) { e.preventDefault(); - let username = this.username; + let { username } = this; if (Meteor.user().u) { username = Meteor.user().u.username; } console.log(username); - Meteor.call('getLoginToken', username, function (error, token) { + Meteor.call('getLoginToken', username, function(error, token) { if (error) { return handleError(error); } @@ -45,8 +46,7 @@ Template.serviceAccountSidebarLogin.events({ document.location.reload(true); if (Meteor.user().u) { localStorage.setItem('serviceAccountForceLogin', true); - } - else { + } else { localStorage.removeItem('serviceAccountForceLogin'); } }); @@ -57,12 +57,7 @@ Template.serviceAccountSidebarLogin.events({ Template.serviceAccountSidebarLogin.onCreated(function() { const instance = this; this.ready = new ReactiveVar(true); - this.token = new ReactiveVar(''); - this.limit = new ReactiveVar(50); - this.filter = new ReactiveVar(''); this.autorun(() => { - const filter = instance.filter.get(); - const limit = instance.limit.get(); const subscription = instance.subscribe('userServiceAccounts'); instance.ready.set(subscription.ready()); }); diff --git a/app/service-accounts/server/methods/getLoginToken.js b/app/service-accounts/server/methods/getLoginToken.js index 8a2246b24fed..c3bb7025cadd 100644 --- a/app/service-accounts/server/methods/getLoginToken.js +++ b/app/service-accounts/server/methods/getLoginToken.js @@ -1,10 +1,10 @@ import { Meteor } from 'meteor/meteor'; +import { Accounts } from 'meteor/accounts-base'; import { Users } from '../../../models'; Meteor.methods({ getLoginToken(username) { - if (!Meteor.userId()) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getLoginToken' }); } @@ -21,7 +21,7 @@ Meteor.methods({ throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getLoginToken' }); } } - + const stampedToken = Accounts._generateStampedLoginToken(); Accounts._insertLoginToken(user._id, stampedToken); return stampedToken; diff --git a/app/ui-sidenav/client/sidebarHeader.js b/app/ui-sidenav/client/sidebarHeader.js index fe478db4ac41..6c0501c0a71f 100644 --- a/app/ui-sidenav/client/sidebarHeader.js +++ b/app/ui-sidenav/client/sidebarHeader.js @@ -73,7 +73,7 @@ const toolbarButtons = (user) => [{ { name: t('Service_account_login'), icon: 'reload', - condition: () => !Meteor.user().u || Meteor.user().u && localStorage.getItem('serviceAccountForceLogin'), + condition: () => !Meteor.user().u || (Meteor.user().u && localStorage.getItem('serviceAccountForceLogin')), action: (e) => { const options = []; const config = { From c2450940cf8e59c66aeeb2711fa1e5bfea6653ad Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 18 Jun 2019 01:27:20 +0530 Subject: [PATCH 26/86] CLI errors fixed --- app/service-accounts/server/publications/userServiceAccounts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/service-accounts/server/publications/userServiceAccounts.js b/app/service-accounts/server/publications/userServiceAccounts.js index 320617c866f6..19f1ecd9912b 100644 --- a/app/service-accounts/server/publications/userServiceAccounts.js +++ b/app/service-accounts/server/publications/userServiceAccounts.js @@ -3,7 +3,7 @@ import { Meteor } from 'meteor/meteor'; import { Users } from '../../../models'; import { getDefaultUserFields } from '../../../utils/server/functions/getDefaultUserFields'; -Meteor.publish('userServiceAccounts', function () { +Meteor.publish('userServiceAccounts', function() { if (!this.userId) { return this.ready(); } From d904e454a046bc6b8fadbd1e600cf592eb439b34 Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 18 Jun 2019 20:52:18 +0530 Subject: [PATCH 27/86] [New] Service Account directory feature --- packages/rocketchat-i18n/i18n/en.i18n.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 42082e156e3c..5e1af157254a 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2633,6 +2633,7 @@ "Server_Type": "Server Type", "Service": "Service", "Service_account": "Service Account", + "Service_accounts": "Service Accounts", "Service_account_key": "Service account key", "Service_account_applied": "Service Accounts approval applications", "Service_account_created_successfully": "Service Account created successfully", @@ -2640,6 +2641,7 @@ "Service_account_description": "Service Accounts are an upgrade to existing user accounts. You can connect to a large number of users using service account with exclusive features such as broadcast message to all your subscribers at once", "Service_account_limit": "Service Accounts per user", "Service_account_login": "Service Account login", + "Service_Accounts_SearchFields": "Fields to consider for service account search", "Service_account_name_placeholder": "Service Account name", "Service_account_username_placeholder": "Service Account username", "Service_account_title": "Create a new Service Account", From 8ba618ab085ff270575847825fb13b601bcddb3f Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 18 Jun 2019 20:59:29 +0530 Subject: [PATCH 28/86] CLI errors fixed --- app/models/server/models/Users.js | 14 +++++++------- app/ui/client/views/app/directory.js | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js index 1f9264433e58..8475e4eaeada 100644 --- a/app/models/server/models/Users.js +++ b/app/models/server/models/Users.js @@ -519,7 +519,7 @@ export class Users extends Base { username: { $exists: true, $nin: exceptions }, }, { - u: { $exists: false } + u: { $exists: false }, }, ...extraQuery, ], @@ -538,7 +538,7 @@ export class Users extends Base { ], }, { - u: { $exists: false } + u: { $exists: false }, }, ]; return this.findByActiveUsersExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery); @@ -549,7 +549,7 @@ export class Users extends Base { { federation: { $exists: true } }, { 'federation.peer': { $ne: localPeer } }, { - u: { $exists: false } + u: { $exists: false }, }, ]; return this.findByActiveUsersExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery); @@ -579,7 +579,7 @@ export class Users extends Base { username: { $exists: true, $nin: exceptions }, }, { - u: { $exists: true } + u: { $exists: true }, }, ...extraQuery, ], @@ -588,12 +588,12 @@ export class Users extends Base { return this._db.find(query, options); } - findByActiveExternalUsersExcept(searchTerm, exceptions, options, forcedSearchFields, localPeer) { + findByActiveExternalServiceAccountsExcept(searchTerm, exceptions, options, forcedSearchFields, localPeer) { const extraQuery = [ { federation: { $exists: true } }, { 'federation.peer': { $ne: localPeer } }, { - u: { $exists: true } + u: { $exists: true }, }, ]; return this.findByActiveServiceAccountsExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery); @@ -608,7 +608,7 @@ export class Users extends Base { ], }, { - u: { $exists: true } + u: { $exists: true }, }, ]; return this.findByActiveServiceAccountsExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery); diff --git a/app/ui/client/views/app/directory.js b/app/ui/client/views/app/directory.js index 6936335c66fe..784d8de8c362 100644 --- a/app/ui/client/views/app/directory.js +++ b/app/ui/client/views/app/directory.js @@ -45,7 +45,7 @@ function directorySearch(config, cb) { description: result.description, subscribers: result.subscribersCount || 0, domain: result.federation && result.federation.peer, - } + }; } return null; })); From 0fe5546201f35855def817bfba430e2a31030809 Mon Sep 17 00:00:00 2001 From: Aditya Date: Wed, 19 Jun 2019 00:17:56 +0530 Subject: [PATCH 29/86] UsernameExists meteor method fixed --- app/service-accounts/server/methods/usernameExists.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/service-accounts/server/methods/usernameExists.js b/app/service-accounts/server/methods/usernameExists.js index 82dfc7f58445..50733b56574c 100644 --- a/app/service-accounts/server/methods/usernameExists.js +++ b/app/service-accounts/server/methods/usernameExists.js @@ -1,18 +1,17 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; -import { Users } from '../../../models'; +import { checkUsernameAvailability } from '../../../lib/server'; Meteor.methods({ - usernameExists(name) { - check(name, String); + usernameExists(username) { + check(username, String); if (!Meteor.userId()) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'roomExists', }); } - const user = Users.findOneByUsername(name, {}); - return !!user; + return !checkUsernameAvailability(username); }, }); From 6211ab651a15a6843443376f5d6976655d440be4 Mon Sep 17 00:00:00 2001 From: Aditya Date: Wed, 19 Jun 2019 01:45:12 +0530 Subject: [PATCH 30/86] Sync commit --- app/service-accounts/server/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/service-accounts/server/index.js b/app/service-accounts/server/index.js index 68f36bfdcf00..557c3901f4c6 100644 --- a/app/service-accounts/server/index.js +++ b/app/service-accounts/server/index.js @@ -6,8 +6,6 @@ import './api/rest'; import './methods/usernameExists'; import './methods/addServiceAccount'; -<<<<<<< HEAD import './hooks/serviceAccountCallback'; -======= + import './publications/fullServiceAccountData'; ->>>>>>> 1a338944d191fa0d0538fd009da8ff80440c314d From db76fa9c3c3436d877b78e3fedba411accb62bf0 Mon Sep 17 00:00:00 2001 From: Aditya Date: Wed, 19 Jun 2019 20:00:04 +0530 Subject: [PATCH 31/86] [NEW] Service Account subscription method added --- app/ui/client/views/app/directory.js | 3 +++ server/methods/createDirectMessage.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/app/ui/client/views/app/directory.js b/app/ui/client/views/app/directory.js index 784d8de8c362..77911fbe5f6c 100644 --- a/app/ui/client/views/app/directory.js +++ b/app/ui/client/views/app/directory.js @@ -148,6 +148,9 @@ Template.directory.helpers({ if (searchType.get() === 'channels') { type = 'c'; routeConfig = { name: item.name }; + } else if (searchType.get() === 'users') { + type = 'd'; + routeConfig = { name: item.username }; } else { type = 'd'; routeConfig = { name: item.username }; diff --git a/server/methods/createDirectMessage.js b/server/methods/createDirectMessage.js index 2fb895435984..e4f022033686 100644 --- a/server/methods/createDirectMessage.js +++ b/server/methods/createDirectMessage.js @@ -109,6 +109,10 @@ Meteor.methods({ upsertSubscription.$set.archived = true; } + if (to.u !== undefined) { + upsertSubscription.$set.sa = true; + } + Subscriptions.upsert({ rid, $and: [{ 'u._id': me._id }], // work around to solve problems with upsert and dot From 6809fdd1392e5822e5d4be72de0e0c1a471179ed Mon Sep 17 00:00:00 2001 From: Aditya Date: Wed, 19 Jun 2019 20:41:22 +0530 Subject: [PATCH 32/86] [NEW] Service Account Broadcast Feature Added --- .../server/hooks/serviceAccountBroadcast.js | 16 ++++++++++++++++ app/service-accounts/server/index.js | 2 ++ 2 files changed, 18 insertions(+) create mode 100644 app/service-accounts/server/hooks/serviceAccountBroadcast.js diff --git a/app/service-accounts/server/hooks/serviceAccountBroadcast.js b/app/service-accounts/server/hooks/serviceAccountBroadcast.js new file mode 100644 index 000000000000..da5b001acdbc --- /dev/null +++ b/app/service-accounts/server/hooks/serviceAccountBroadcast.js @@ -0,0 +1,16 @@ +import { callbacks } from '../../../callbacks/server'; +import { Rooms } from '../../../models/server'; +import { sendMessage } from '../../../lib/server'; + +callbacks.add('beforeSaveMessage', (message, room) => { + // abort if room is not with a service account broadcast room + if (!room || !room.sa) { + return message; + } + + const rooms = Rooms.findDirectRoomContainingUsername(Meteor.user().username); + for (const targetRoom of rooms) { + sendMessage(Meteor.user(), { msg: message.msg }, targetRoom); + } + return message; +}); \ No newline at end of file diff --git a/app/service-accounts/server/index.js b/app/service-accounts/server/index.js index 71a5dcb7f1af..525cab750bb3 100644 --- a/app/service-accounts/server/index.js +++ b/app/service-accounts/server/index.js @@ -7,3 +7,5 @@ import './methods/usernameExists'; import './methods/addServiceAccount'; import './publications/fullServiceAccountData'; + +import './hooks/serviceAccountBroadcast'; From 62d6237b8d0d583416d4814b34b0387f1e0517f8 Mon Sep 17 00:00:00 2001 From: Aditya Date: Wed, 19 Jun 2019 20:41:42 +0530 Subject: [PATCH 33/86] [NEW] Service Account Broadcast Feature Added --- app/service-accounts/server/hooks/serviceAccountBroadcast.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/service-accounts/server/hooks/serviceAccountBroadcast.js b/app/service-accounts/server/hooks/serviceAccountBroadcast.js index da5b001acdbc..734ab02d64e7 100644 --- a/app/service-accounts/server/hooks/serviceAccountBroadcast.js +++ b/app/service-accounts/server/hooks/serviceAccountBroadcast.js @@ -13,4 +13,4 @@ callbacks.add('beforeSaveMessage', (message, room) => { sendMessage(Meteor.user(), { msg: message.msg }, targetRoom); } return message; -}); \ No newline at end of file +}); From f3e8ef87593715a987181dd7ec52099e92a1a733 Mon Sep 17 00:00:00 2001 From: Aditya Date: Wed, 19 Jun 2019 23:23:43 +0530 Subject: [PATCH 34/86] [NEW] Service account subscription sidenav type --- app/api/server/v1/users.js | 1 + app/service-accounts/client/index.js | 3 ++ .../client/views/serviceAccountsList.html | 10 +++++++ .../client/views/serviceAccountsList.js | 29 +++++++++++++++++++ .../lib/serviceAccountRoomType.js | 22 ++++++++++++++ app/service-accounts/server/config.js | 7 +++++ app/service-accounts/server/index.js | 2 ++ app/ui-account/client/accountPreferences.html | 7 +++++ app/ui-account/client/accountPreferences.js | 1 + app/ui-sidenav/client/roomList.js | 5 ++++ server/methods/saveUserPreferences.js | 1 + server/publications/subscription.js | 1 + tests/end-to-end/api/00-miscellaneous.js | 1 + 13 files changed, 90 insertions(+) create mode 100644 app/service-accounts/client/views/serviceAccountsList.html create mode 100644 app/service-accounts/client/views/serviceAccountsList.js create mode 100644 app/service-accounts/lib/serviceAccountRoomType.js diff --git a/app/api/server/v1/users.js b/app/api/server/v1/users.js index 1cf3926f035d..92f8420e3ac0 100644 --- a/app/api/server/v1/users.js +++ b/app/api/server/v1/users.js @@ -451,6 +451,7 @@ API.v1.addRoute('users.setPreferences', { authRequired: true }, { sidebarHideAvatar: Match.Optional(Boolean), sidebarGroupByType: Match.Optional(Boolean), sidebarShowDiscussion: Match.Optional(Boolean), + sidebarShowServiceAccounts: Match.Optional(Boolean), muteFocusedConversations: Match.Optional(Boolean), }), }); diff --git a/app/service-accounts/client/index.js b/app/service-accounts/client/index.js index 011b64062925..efc85491d365 100644 --- a/app/service-accounts/client/index.js +++ b/app/service-accounts/client/index.js @@ -4,3 +4,6 @@ import './route'; // views import './views/serviceAccountDashboard'; import './views/creationDialog/createServiceAccount'; +import './views/serviceAccountsList'; + +import '../lib/serviceAccountRoomType'; diff --git a/app/service-accounts/client/views/serviceAccountsList.html b/app/service-accounts/client/views/serviceAccountsList.html new file mode 100644 index 000000000000..98819ed29d0c --- /dev/null +++ b/app/service-accounts/client/views/serviceAccountsList.html @@ -0,0 +1,10 @@ + diff --git a/app/service-accounts/client/views/serviceAccountsList.js b/app/service-accounts/client/views/serviceAccountsList.js new file mode 100644 index 000000000000..9ccc500b69b4 --- /dev/null +++ b/app/service-accounts/client/views/serviceAccountsList.js @@ -0,0 +1,29 @@ +import { Meteor } from 'meteor/meteor'; +import { Template } from 'meteor/templating'; + +import { ChatSubscription } from '../../../models/client'; +import { getUserPreference } from '../../../utils/client'; +import { settings } from '../../../settings/client'; + +import './serviceAccountsList.html'; + +Template.serviceAccountsList.helpers({ + rooms() { + const user = Meteor.userId(); + const sortBy = getUserPreference(user, 'sidebarSortby') || 'alphabetical'; + const query = { + open: true, + }; + + const sort = {}; + + if (sortBy === 'activity') { + sort.lm = -1; + } else { // alphabetical + sort[this.identifier === 'd' && settings.get('UI_Use_Real_Name') ? 'lowerCaseFName' : 'lowerCaseName'] = /descending/.test(sortBy) ? -1 : 1; + } + + query.sa = { $exists: true }; + return ChatSubscription.find(query, { sort }); + }, +}); diff --git a/app/service-accounts/lib/serviceAccountRoomType.js b/app/service-accounts/lib/serviceAccountRoomType.js new file mode 100644 index 000000000000..f3f18c3e6f25 --- /dev/null +++ b/app/service-accounts/lib/serviceAccountRoomType.js @@ -0,0 +1,22 @@ +import { Meteor } from 'meteor/meteor'; + +import { RoomTypeConfig, roomTypes, getUserPreference } from '../../utils'; + +export class ServiceAccountRoomType extends RoomTypeConfig { + constructor() { + super({ + identifier: 'sa', + order: 60, + label: 'Subscriptions', + }); + + // we need a custom template in order to have a custom query showing the subscriptions to serviceAccounts + this.customTemplate = 'serviceAccountsList'; + } + + condition() { + return getUserPreference(Meteor.userId(), 'sidebarShowServiceAccounts'); + } +} + +roomTypes.add(new ServiceAccountRoomType()); diff --git a/app/service-accounts/server/config.js b/app/service-accounts/server/config.js index 3f7c7b16d360..835cb7f96f2e 100644 --- a/app/service-accounts/server/config.js +++ b/app/service-accounts/server/config.js @@ -15,4 +15,11 @@ Meteor.startup(() => { public: true, }); }); + settings.add('Accounts_Default_User_Preferences_sidebarShowServiceAccounts', true, { + group: 'Accounts', + section: 'Accounts_Default_User_Preferences', + type: 'boolean', + public: true, + i18nLabel: 'Group_serviceAccounts', + }); }); diff --git a/app/service-accounts/server/index.js b/app/service-accounts/server/index.js index 71a5dcb7f1af..0ce3a57d1633 100644 --- a/app/service-accounts/server/index.js +++ b/app/service-accounts/server/index.js @@ -7,3 +7,5 @@ import './methods/usernameExists'; import './methods/addServiceAccount'; import './publications/fullServiceAccountData'; + +import '../lib/serviceAccountRoomType'; diff --git a/app/ui-account/client/accountPreferences.html b/app/ui-account/client/accountPreferences.html index 76e95a26c038..3fbc180d8d0f 100644 --- a/app/ui-account/client/accountPreferences.html +++ b/app/ui-account/client/accountPreferences.html @@ -278,6 +278,13 @@

{{_ "Sidebar"}}

+
+ +
+ + +
+
diff --git a/app/ui-account/client/accountPreferences.js b/app/ui-account/client/accountPreferences.js index 247fc0785179..a7ab32992f64 100644 --- a/app/ui-account/client/accountPreferences.js +++ b/app/ui-account/client/accountPreferences.js @@ -169,6 +169,7 @@ Template.accountPreferences.onCreated(function() { data.mobileNotifications = $('#mobileNotifications').find('select').val(); data.unreadAlert = JSON.parse($('#unreadAlert').find('input:checked').val()); data.sidebarShowDiscussion = JSON.parse($('#sidebarShowDiscussion').find('input:checked').val()); + data.sidebarShowServiceAccounts = JSON.parse($('#sidebarShowServiceAccounts').find('input:checked').val()); data.notificationsSoundVolume = parseInt($('#notificationsSoundVolume').val()); data.roomCounterSidebar = JSON.parse($('#roomCounterSidebar').find('input:checked').val()); data.highlights = _.compact(_.map($('[name=highlights]').val().split(/,|\n/), function(e) { diff --git a/app/ui-sidenav/client/roomList.js b/app/ui-sidenav/client/roomList.js index ba946ed7eeee..f7d282b6d6d3 100644 --- a/app/ui-sidenav/client/roomList.js +++ b/app/ui-sidenav/client/roomList.js @@ -25,6 +25,7 @@ Template.roomList.helpers({ 'settings.preferences.sidebarShowFavorites': 1, 'settings.preferences.sidebarShowUnread': 1, 'settings.preferences.sidebarShowDiscussion': 1, + 'settings.preferences.sidebarShowServiceAccounts': 1, 'services.tokenpass': 1, messageViewMode: 1, }, @@ -84,6 +85,10 @@ Template.roomList.helpers({ query.prid = { $exists: false }; } + if (getUserPreference(user, 'sidebarShowServiceAccounts')) { + query.sa = { $exists: false }; + } + if (getUserPreference(user, 'sidebarShowUnread')) { query.$or = [ { alert: { $ne: true } }, diff --git a/server/methods/saveUserPreferences.js b/server/methods/saveUserPreferences.js index f9e0d6c97683..cc3fc86a8f08 100644 --- a/server/methods/saveUserPreferences.js +++ b/server/methods/saveUserPreferences.js @@ -38,6 +38,7 @@ Meteor.methods({ sidebarHideAvatar: Match.Optional(Boolean), sidebarGroupByType: Match.Optional(Boolean), sidebarShowDiscussion: Match.Optional(Boolean), + sidebarShowServiceAccounts: Match.Optional(Boolean), muteFocusedConversations: Match.Optional(Boolean), }; check(settings, Match.ObjectIncluding(keys)); diff --git a/server/publications/subscription.js b/server/publications/subscription.js index 182de0eaf38b..aead3afcbea9 100644 --- a/server/publications/subscription.js +++ b/server/publications/subscription.js @@ -18,6 +18,7 @@ const fields = { roles: 1, unread: 1, prid: 1, + sa: 1, userMentions: 1, groupMentions: 1, archived: 1, diff --git a/tests/end-to-end/api/00-miscellaneous.js b/tests/end-to-end/api/00-miscellaneous.js index 3a6773fa2bac..91e09e3ca510 100644 --- a/tests/end-to-end/api/00-miscellaneous.js +++ b/tests/end-to-end/api/00-miscellaneous.js @@ -141,6 +141,7 @@ describe('miscellaneous', function() { 'sidebarGroupByType', 'muteFocusedConversations', 'sidebarShowDiscussion', + 'sidebarShowServiceAccounts', ]; expect(res.body).to.have.property('success', true); From 46b87f2ade48015aa28fdee1803770ab90dbaa8f Mon Sep 17 00:00:00 2001 From: Aditya Date: Thu, 27 Jun 2019 02:39:53 +0530 Subject: [PATCH 35/86] Broadcast Room name change handled --- app/lib/server/functions/setUsername.js | 2 ++ app/models/server/models/Rooms.js | 12 ++++++++++++ app/models/server/models/Subscriptions.js | 12 ++++++++++++ app/ui-account/client/accountProfile.js | 4 ++-- app/utils/server/functions/getDefaultUserFields.js | 1 + 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/lib/server/functions/setUsername.js b/app/lib/server/functions/setUsername.js index 0c4424fc3690..24e58416102a 100644 --- a/app/lib/server/functions/setUsername.js +++ b/app/lib/server/functions/setUsername.js @@ -76,8 +76,10 @@ export const _setUsername = function(userId, u) { Rooms.replaceUsername(previousUsername, username); Rooms.replaceMutedUsername(previousUsername, username); Rooms.replaceUsernameOfUserByUserId(user._id, username); + Rooms.replaceServiceAccountBroadcastRoomName(previousUsername, username); Subscriptions.setUserUsernameByUserId(user._id, username); Subscriptions.setNameForDirectRoomsWithOldName(previousUsername, username); + Subscriptions.replaceServiceAccountBroadcastRoomName(previousUsername, username); LivechatDepartmentAgents.replaceUsernameOfAgentByUserId(user._id, username); Users.setOwnerUsernameByUserId(user._id, username); diff --git a/app/models/server/models/Rooms.js b/app/models/server/models/Rooms.js index 2d7f38770932..3c46ba11c19e 100644 --- a/app/models/server/models/Rooms.js +++ b/app/models/server/models/Rooms.js @@ -1072,6 +1072,18 @@ export class Rooms extends Base { return this.update(query, update, { multi: true }); } + replaceServiceAccountBroadcastRoomName(previousUsername, username) { + const query = { name: `broadcast_${previousUsername}` }; + + const update = { + $set: { + name: `broadcast_${username}`, + }, + }; + + return this.update(query, update); + } + setJoinCodeById(_id, joinCode) { let update; const query = { _id }; diff --git a/app/models/server/models/Subscriptions.js b/app/models/server/models/Subscriptions.js index 93813847b88a..04c218c1f2cc 100644 --- a/app/models/server/models/Subscriptions.js +++ b/app/models/server/models/Subscriptions.js @@ -1206,6 +1206,18 @@ export class Subscriptions extends Base { return this.update(query, update, { multi: true }); } + replaceServiceAccountBroadcastRoomName(previousUsername, username) { + const query = { name: `broadcast_${previousUsername}` }; + + const update = { + $set: { + name: `broadcast_${username}`, + }, + }; + + return this.update(query, update); + } + // INSERT createWithRoomAndUser(room, user, extraData) { const subscription = { diff --git a/app/ui-account/client/accountProfile.js b/app/ui-account/client/accountProfile.js index 653167237042..48d1860aba1a 100644 --- a/app/ui-account/client/accountProfile.js +++ b/app/ui-account/client/accountProfile.js @@ -126,10 +126,10 @@ Template.accountProfile.helpers({ return; } } - if (!avatar && user.name === realname && user.username === username && getUserEmailAddress(user) === email === email && (!password || password !== confirmationPassword)) { + if (!avatar && user.name === realname && user.username === username && (!!user.u || getUserEmailAddress(user) === email === email) && (!password || password !== confirmationPassword)) { return ret; } - if (!validateEmail(email) || (!validateUsername(username) || usernameAvaliable !== true) || !validateName(realname)) { + if ((!validateEmail(email) && !user.u) || (!validateUsername(username) || usernameAvaliable !== true) || !validateName(realname)) { return ret; } }, diff --git a/app/utils/server/functions/getDefaultUserFields.js b/app/utils/server/functions/getDefaultUserFields.js index f3332ca76177..124315039d70 100644 --- a/app/utils/server/functions/getDefaultUserFields.js +++ b/app/utils/server/functions/getDefaultUserFields.js @@ -25,4 +25,5 @@ export const getDefaultUserFields = () => ({ 'services.totp.enabled': 1, statusLivechat: 1, banners: 1, + u: 1, }); From 0562355fce19cfc4e0d000dee7867509d2cfdd58 Mon Sep 17 00:00:00 2001 From: Aditya Date: Thu, 27 Jun 2019 02:46:17 +0530 Subject: [PATCH 36/86] Lint errors fixed --- app/models/server/models/Rooms.js | 8 ++++---- app/models/server/models/Subscriptions.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/models/server/models/Rooms.js b/app/models/server/models/Rooms.js index 3c46ba11c19e..24b205dcf97e 100644 --- a/app/models/server/models/Rooms.js +++ b/app/models/server/models/Rooms.js @@ -1073,14 +1073,14 @@ export class Rooms extends Base { } replaceServiceAccountBroadcastRoomName(previousUsername, username) { - const query = { name: `broadcast_${previousUsername}` }; - + const query = { name: `broadcast_${ previousUsername }` }; + const update = { $set: { - name: `broadcast_${username}`, + name: `broadcast_${ username }`, }, }; - + return this.update(query, update); } diff --git a/app/models/server/models/Subscriptions.js b/app/models/server/models/Subscriptions.js index 04c218c1f2cc..03fb3e9c6a01 100644 --- a/app/models/server/models/Subscriptions.js +++ b/app/models/server/models/Subscriptions.js @@ -1207,14 +1207,14 @@ export class Subscriptions extends Base { } replaceServiceAccountBroadcastRoomName(previousUsername, username) { - const query = { name: `broadcast_${previousUsername}` }; - + const query = { name: `broadcast_${ previousUsername }` }; + const update = { $set: { - name: `broadcast_${username}`, + name: `broadcast_${ username }`, }, }; - + return this.update(query, update); } From c603a51dc6ccb168cf85792d8d02de849cf1cdb6 Mon Sep 17 00:00:00 2001 From: Aditya Date: Thu, 27 Jun 2019 12:53:35 +0530 Subject: [PATCH 37/86] getLoginToken method refactored --- .../views/serviceAccountSidebarLogin.js | 1 - app/service-accounts/server/index.js | 3 --- .../server/methods/getLoginToken.js | 20 ++++++++----------- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/app/service-accounts/client/views/serviceAccountSidebarLogin.js b/app/service-accounts/client/views/serviceAccountSidebarLogin.js index 9a34f73f7008..c98a0d7b4e3c 100644 --- a/app/service-accounts/client/views/serviceAccountSidebarLogin.js +++ b/app/service-accounts/client/views/serviceAccountSidebarLogin.js @@ -33,7 +33,6 @@ Template.serviceAccountSidebarLogin.events({ if (Meteor.user().u) { username = Meteor.user().u.username; } - console.log(username); Meteor.call('getLoginToken', username, function(error, token) { if (error) { return handleError(error); diff --git a/app/service-accounts/server/index.js b/app/service-accounts/server/index.js index 02500bd847a9..f6a6fb8418af 100644 --- a/app/service-accounts/server/index.js +++ b/app/service-accounts/server/index.js @@ -10,7 +10,4 @@ import './methods/getLoginToken'; import './hooks/serviceAccountCallback'; import './publications/fullServiceAccountData'; -<<<<<<< HEAD import './publications/userServiceAccounts'; -======= ->>>>>>> 6aa19d374655ed56ab0ff5e33713ab11044f2aa3 diff --git a/app/service-accounts/server/methods/getLoginToken.js b/app/service-accounts/server/methods/getLoginToken.js index c3bb7025cadd..ad8a0bf6919b 100644 --- a/app/service-accounts/server/methods/getLoginToken.js +++ b/app/service-accounts/server/methods/getLoginToken.js @@ -9,21 +9,17 @@ Meteor.methods({ throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getLoginToken' }); } + let stampedToken = {}; const user = Users.findOneByUsername(username, {}); - if (user.u) { - if (user.u._id !== Meteor.userId()) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getLoginToken' }); - } - } + const isOwnerAccount = user.u && user.u._id === Meteor.userId(); // check if the requested account is owned by the user + const isServiceAccount = Meteor.user().u && user._id === Meteor.user().u._id; // check if the service account is requesting owner account login token - if (Meteor.user().u) { - if (user._id !== Meteor.user().u._id) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getLoginToken' }); - } + if (isOwnerAccount || isServiceAccount) { + stampedToken = Accounts._generateStampedLoginToken(); + Accounts._insertLoginToken(user._id, stampedToken); + } else { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getLoginToken' }); } - - const stampedToken = Accounts._generateStampedLoginToken(); - Accounts._insertLoginToken(user._id, stampedToken); return stampedToken; }, }); From bf05a2d2ccc0c9bcc5cb2ac763e1d361d1020ccf Mon Sep 17 00:00:00 2001 From: Aditya Date: Thu, 27 Jun 2019 12:59:58 +0530 Subject: [PATCH 38/86] Console statements removed --- app/service-accounts/client/views/serviceAccountSidebarLogin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/service-accounts/client/views/serviceAccountSidebarLogin.js b/app/service-accounts/client/views/serviceAccountSidebarLogin.js index c98a0d7b4e3c..ee652351b48b 100644 --- a/app/service-accounts/client/views/serviceAccountSidebarLogin.js +++ b/app/service-accounts/client/views/serviceAccountSidebarLogin.js @@ -40,7 +40,7 @@ Template.serviceAccountSidebarLogin.events({ FlowRouter.go('/home'); Meteor.loginWithToken(token.token, (err) => { if (err) { - console.log(err); + return handleError(err); } document.location.reload(true); if (Meteor.user().u) { From 73eedd8296f1e4fa10f5c6789e6126a74fa6707a Mon Sep 17 00:00:00 2001 From: Aditya Date: Thu, 27 Jun 2019 13:16:03 +0530 Subject: [PATCH 39/86] Sidebar header permission modified --- app/ui-sidenav/client/sidebarHeader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/ui-sidenav/client/sidebarHeader.js b/app/ui-sidenav/client/sidebarHeader.js index a862d197d683..64392c46b89f 100644 --- a/app/ui-sidenav/client/sidebarHeader.js +++ b/app/ui-sidenav/client/sidebarHeader.js @@ -7,7 +7,7 @@ import { popover, AccountBox, menu, SideNav, modal } from '../../ui-utils'; import { t, getUserPreference, handleError } from '../../utils'; import { callbacks } from '../../callbacks'; import { settings } from '../../settings'; -import { hasAtLeastOnePermission, hasPermission } from '../../authorization'; +import { hasAtLeastOnePermission } from '../../authorization'; const setStatus = (status) => { AccountBox.setStatus(status); @@ -201,7 +201,7 @@ const toolbarButtons = (user) => [{ }); } - if (serviceAccountEnabled && hasPermission('create-service-account')) { + if (serviceAccountEnabled && hasAtLeastOnePermission(['create-service-account'])) { items.push({ icon: 'user', name: t('Service_account'), From 68bed0afbce335b05b43429855f2954392c6e412 Mon Sep 17 00:00:00 2001 From: Aditya Date: Thu, 27 Jun 2019 13:25:58 +0530 Subject: [PATCH 40/86] Merge branch service-accounts --- app/service-accounts/server/index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/service-accounts/server/index.js b/app/service-accounts/server/index.js index c71726935fba..f6a6fb8418af 100644 --- a/app/service-accounts/server/index.js +++ b/app/service-accounts/server/index.js @@ -10,7 +10,4 @@ import './methods/getLoginToken'; import './hooks/serviceAccountCallback'; import './publications/fullServiceAccountData'; -<<<<<<< HEAD import './publications/userServiceAccounts'; -======= ->>>>>>> e6e2229df71a4435c6fe8d0bf13214c1116de199 From 1dc5d2a23cc2560422bdf1b719be77852515888f Mon Sep 17 00:00:00 2001 From: Aditya Date: Thu, 27 Jun 2019 13:34:17 +0530 Subject: [PATCH 41/86] Added service account directory search translation key --- packages/rocketchat-i18n/i18n/en.i18n.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index f3cbd06c557a..be449e405f07 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2603,6 +2603,7 @@ "Search_Page_Size": "Page Size", "Search_Private_Groups": "Search Private Groups", "Search_Provider": "Search Provider", + "Search_ServiceAccounts": "Search Service Accounts", "Search_Users": "Search Users", "seconds": "seconds", "Secret_token": "Secret Token", From 1b42205d1259f66733cb209d06f4d5f6c284e509 Mon Sep 17 00:00:00 2001 From: Aditya Date: Thu, 27 Jun 2019 14:05:31 +0530 Subject: [PATCH 42/86] Subscribers count added --- app/ui/client/views/app/directory.js | 2 +- server/methods/browseChannels.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/ui/client/views/app/directory.js b/app/ui/client/views/app/directory.js index 77911fbe5f6c..19215393efe1 100644 --- a/app/ui/client/views/app/directory.js +++ b/app/ui/client/views/app/directory.js @@ -43,7 +43,7 @@ function directorySearch(config, cb) { username: result.username, createdAt: timeAgo(result.createdAt, t), description: result.description, - subscribers: result.subscribersCount || 0, + subscribers: result.subscribers || 0, domain: result.federation && result.federation.peer, }; } diff --git a/server/methods/browseChannels.js b/server/methods/browseChannels.js index 7ddcb836b837..53b09e32755a 100644 --- a/server/methods/browseChannels.js +++ b/server/methods/browseChannels.js @@ -106,7 +106,6 @@ Meteor.methods({ name: 1, createdAt: 1, description: 1, - subscribers: 1, federation: 1, }, }; @@ -124,7 +123,9 @@ Meteor.methods({ } const total = result.count(); const results = result.fetch(); - + results.forEach((account) => { + account.subscribers = Rooms.findDirectRoomContainingUsername(account.username).count(); + }); return { total, results, From e893a18d6b7db7dddf2c615efa658a8b59659d0d Mon Sep 17 00:00:00 2001 From: Aditya Date: Fri, 28 Jun 2019 16:31:32 +0530 Subject: [PATCH 43/86] [NEW] Service Account sidenav type --- app/service-accounts/server/config.js | 2 +- app/service-accounts/server/index.js | 5 +---- app/ui-account/client/accountPreferences.html | 2 +- app/ui-sidenav/client/sortlist.html | 9 +++++++++ app/ui-sidenav/client/sortlist.js | 3 +++ packages/rocketchat-i18n/i18n/en.i18n.json | 1 + 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/service-accounts/server/config.js b/app/service-accounts/server/config.js index 26e266716b0e..45f2ab361af5 100644 --- a/app/service-accounts/server/config.js +++ b/app/service-accounts/server/config.js @@ -24,6 +24,6 @@ Meteor.startup(() => { section: 'Accounts_Default_User_Preferences', type: 'boolean', public: true, - i18nLabel: 'Group_serviceAccounts', + i18nLabel: 'Group_subscriptions', }); }); diff --git a/app/service-accounts/server/index.js b/app/service-accounts/server/index.js index a95d599f6f13..ec817f2af218 100644 --- a/app/service-accounts/server/index.js +++ b/app/service-accounts/server/index.js @@ -10,9 +10,6 @@ import './methods/getLoginToken'; import './hooks/serviceAccountCallback'; import './publications/fullServiceAccountData'; -<<<<<<< HEAD +import './publications/userServiceAccounts'; import '../lib/serviceAccountRoomType'; -======= -import './publications/userServiceAccounts'; ->>>>>>> 117d725d6cc18dfcfdd2d314fdfe68dfa1e3cd6f diff --git a/app/ui-account/client/accountPreferences.html b/app/ui-account/client/accountPreferences.html index 3fbc180d8d0f..8f73514889ad 100644 --- a/app/ui-account/client/accountPreferences.html +++ b/app/ui-account/client/accountPreferences.html @@ -279,7 +279,7 @@

{{_ "Sidebar"}}

- +
diff --git a/app/ui-sidenav/client/sortlist.html b/app/ui-sidenav/client/sortlist.html index fa8efb05de23..40b58017368d 100644 --- a/app/ui-sidenav/client/sortlist.html +++ b/app/ui-sidenav/client/sortlist.html @@ -31,6 +31,15 @@ {{_ "Group_discussions"}} +
  • + +