Skip to content

Commit

Permalink
Service accounts feature (#62)
Browse files Browse the repository at this point in the history
* [NEW] Service Account Admin Settings and Configuration files added

* [NEW] Service Account Admin Settings and Configuration files added

* [NEW] Service Account Admin Settings and Configuration files added

* [NEW] Service Account Admin Settings and Configuration files added

* Service Account Creation dialog added

* [NEW] Service Account Creation method

* Service Account owner username update method added

* Fixed CLI errors

* Fixed CLI errors

* Service Account creation heading fixed

* Service Account broadcast room callback added

* Service Account creation method refactored

* Service Account Callback completed

* Typos fixed

* CLI errors fixed

* [NEW] Service Account one-tap login complete

* Callbacks modified

* Service Accounts directory tab added

* Refactored creation method and added tests

* CLI errors fixed

* CLI errors fixed

* Bugs fixed

* [NEW] Service Accounts Login method

* Typo fixed

* CLI errors fixed

* CLI errors fixed

* [New] Service Account directory feature

* CLI errors fixed

* UsernameExists meteor method fixed

* Sync commit

* [NEW] Service Account subscription method added

* [NEW] Service account subscription sidenav type

* Broadcast Room name change handled

* Lint errors fixed

* getLoginToken method refactored

* Console statements removed

* Sidebar header permission modified

* Merge branch service-accounts

* Added service account directory search translation key

* Subscribers count added

* [NEW] Service Account sidenav type

* Unread counter added in popver

* Get linked service account method added
  • Loading branch information
bhardwajaditya authored and Kailash0311 committed Jul 8, 2019
1 parent da42a95 commit 4b99331
Show file tree
Hide file tree
Showing 49 changed files with 1,246 additions and 36 deletions.
1 change: 1 addition & 0 deletions app/api/server/v1/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,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),
}),
});
Expand Down
12 changes: 11 additions & 1 deletion app/lib/server/functions/saveUser.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,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 = {
Expand All @@ -177,6 +179,10 @@ export const saveUser = function(userId, userData) {
if (userData.email) {
createUser.email = userData.email;
}
if (userData.u) {
createUser.u = userData.u;
createUser.active = userData.active;
}

const _id = Accounts.createUser(createUser);

Expand All @@ -199,6 +205,10 @@ export const saveUser = function(userId, userData) {
updateUser.$set['emails.0.verified'] = userData.verified;
}

if (typeof userData.description !== 'undefined') {
updateUser.$set.description = userData.description;
}

Meteor.users.update({ _id }, updateUser);

if (userData.sendWelcomeEmail) {
Expand Down
3 changes: 3 additions & 0 deletions app/lib/server/functions/setUsername.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,12 @@ 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);

const fileStore = FileUpload.getStore('Avatars');
const file = fileStore.model.findOneByName(previousUsername);
Expand Down
12 changes: 12 additions & 0 deletions app/models/server/models/Rooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down
12 changes: 12 additions & 0 deletions app/models/server/models/Subscriptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
86 changes: 86 additions & 0 deletions app/models/server/models/Users.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class Users extends Base {
this.tryEnsureIndex({ type: 1 });
this.tryEnsureIndex({ 'visitorEmails.address': 1 });
this.tryEnsureIndex({ federation: 1 }, { sparse: true });
this.tryEnsureIndex({ 'u._id': 1 });
}

getLoginTokensByUserId(userId) {
Expand Down Expand Up @@ -518,6 +519,9 @@ export class Users extends Base {
{
username: { $exists: true, $nin: exceptions },
},
{
u: { $exists: false },
},
...extraQuery,
],
};
Expand All @@ -534,6 +538,9 @@ export class Users extends Base {
{ 'federation.peer': localPeer },
],
},
{
u: { $exists: false },
},
];
return this.findByActiveUsersExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery);
}
Expand All @@ -542,10 +549,72 @@ export class Users extends Base {
const extraQuery = [
{ federation: { $exists: true } },
{ 'federation.peer': { $ne: localPeer } },
{
u: { $exists: false },
},
];
return this.findByActiveUsersExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery);
}

findByActiveServiceAccountsExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery = []) {
if (exceptions == null) { exceptions = []; }
if (options == null) { options = {}; }
if (!_.isArray(exceptions)) {
exceptions = [exceptions];
}

const termRegex = new RegExp(s.escapeRegExp(searchTerm), 'i');
const searchFields = forcedSearchFields || settings.get('Service_Accounts_SearchFields').trim().split(',');
const orStmt = _.reduce(searchFields, function(acc, el) {
acc.push({ [el.trim()]: termRegex });
return acc;
}, []);

const query = {
$and: [
{
active: true,
$or: orStmt,
},
{
username: { $exists: true, $nin: exceptions },
},
{
u: { $exists: true },
},
...extraQuery,
],
};

return this._db.find(query, options);
}

findByActiveExternalServiceAccountsExcept(searchTerm, exceptions, options, forcedSearchFields, localPeer) {
const extraQuery = [
{ federation: { $exists: true } },
{ 'federation.peer': { $ne: localPeer } },
{
u: { $exists: true },
},
];
return this.findByActiveServiceAccountsExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery);
}

findByActiveLocalServiceAccountsExcept(searchTerm, exceptions, options, forcedSearchFields, localPeer) {
const extraQuery = [
{
$or: [
{ federation: { $exists: false } },
{ 'federation.peer': localPeer },
],
},
{
u: { $exists: true },
},
];
return this.findByActiveServiceAccountsExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery);
}

findUsersByNameOrUsername(nameOrUsername, options) {
const query = {
username: {
Expand Down Expand Up @@ -666,6 +735,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);
Expand Down Expand Up @@ -1057,6 +1132,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 = {
Expand Down
10 changes: 10 additions & 0 deletions app/service-accounts/client/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import './startup';
import './route';

// views
import './views/serviceAccountDashboard';
import './views/creationDialog/createServiceAccount';
import './views/serviceAccountsList';

import '../lib/serviceAccountRoomType';
import './views/serviceAccountSidebarLogin';
14 changes: 14 additions & 0 deletions app/service-accounts/client/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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_applied'),
});
},
});
11 changes: 11 additions & 0 deletions app/service-accounts/client/startup.js
Original file line number Diff line number Diff line change
@@ -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-sa-request']);
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<template name="createServiceAccount">
<section class="create-channel">
<div class="create-channel__wrapper">
<p class="create-channel__description">{{_ "Service_account_description"}}</p>
<form id="create-service-account" name="create-service-account" class="create-channel__content">
<div class="create-channel__inputs">
<div class="rc-input {{#if invalidChannel}}rc-input--error{{/if}}">
<label class="rc-input__label">
<div class="rc-input__title">{{_ "Username_title"}}</div>
<div class="rc-input__wrapper">
<div class="rc-input__icon">
{{> icon block="rc-input__icon-svg" icon="at"}}
</div>
<input name="username" type="text" class="rc-input__element"
placeholder="{{_ 'Service_account_username_placeholder'}}" autofocus>
</div>
</label>
{{#if inUse}}
<div class="rc-input__error">
<div class="rc-input__error-icon">
{{> icon block="rc-input__error-icon" icon="warning" classes="rc-input__error-icon-svg"}}
</div>
<div class="rc-input__error-message">{{_ "Username_already_exist"}}</div>
</div>
{{/if}}
</div>
</div>
<div class="rc-input">
<label class="rc-input__label">
<div class="rc-input__title">{{_ "Name"}}</div>
<div class="rc-input__wrapper">
<div class="rc-input__icon">
{{> icon block="rc-input__icon-svg" icon="lock"}}
</div>
<input name="name" type="text" class="rc-input__element" placeholder="{{_ "Service_account_name_placeholder"}}">
</div>
</label>
</div>
<div class="rc-input">
<label class="rc-input__label">
<div class="rc-input__title">{{_ "Password"}}</div>
<div class="rc-input__wrapper">
<div class="rc-input__icon">
{{> icon block="rc-input__icon-svg" icon="lock"}}
</div>
<input name="password" type="password" class="rc-input__element" placeholder="{{_ "Password"}}">
</div>
</label>
</div>
<div class="rc-input">
<label class="rc-input__label">
<div class="rc-input__title">{{_ "Confirm_password"}}</div>
<div class="rc-input__wrapper">
<div class="rc-input__icon">
{{> icon block="rc-input__icon-svg" icon="lock"}}
</div>
<input name="confirmPassword" type="password" class="rc-input__element" placeholder="{{_ "Password"}}">
</div>
</label>
{{#if notMatch}}
<div class="rc-input__error">
<div class="rc-input__error-icon">
{{> icon block="rc-input__error-icon" icon="warning" classes="rc-input__error-icon-svg"}}
</div>
<div class="rc-input__error-message">{{_ "Invalid_confirm_pass"}}</div>
</div>
{{/if}}
</div>
<div class="rc-input">
<label class="rc-input__label">
<div class="rc-input__title">{{_ "Description"}}</div>
<div class="rc-input__wrapper">
<div class="rc-input__icon">
{{> icon block="rc-input__icon-svg" icon="edit"}}
</div>
<input name="description" type="text" class="rc-input__element" placeholder="{{_ "Description"}}">
</div>
</label>
</div>
</form>
<div class="create-channel__inputs">
<input form='create-service-account' class="rc-button rc-button--primary" type='submit' data-button="create" {{createIsDisabled}} value="{{_ "Create"}}" />
</div>
</div>
</section>
</template>
Loading

0 comments on commit 4b99331

Please sign in to comment.