Skip to content
This repository was archived by the owner on Nov 21, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/__tests__/permissionMutations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ describe('Test permissions mutations', () => {
});

test('Update group', async () => {
await userFactory({ groupIds: [_group._id] });
await userFactory({ groupIds: [_group._id] });

const user1 = await userFactory({});
const user2 = await userFactory({});

Expand Down Expand Up @@ -201,6 +204,9 @@ describe('Test permissions mutations', () => {
}
`;

await userFactory({ groupIds: [_group._id] });
await userFactory({ groupIds: [_group._id] });

await graphqlRequest(mutation, 'usersGroupsRemove', { _id: _group._id }, context);

expect(await UsersGroups.findOne({ _id: _group._id })).toBe(null);
Expand Down
40 changes: 37 additions & 3 deletions src/data/logUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ interface IContentTypeParams {
contentTypeId: string;
}

/**
* @param object - Previous state of the object
* @param newData - Requested update data
* @param updatedDocument - State after any updates to the object
*/
export interface ILogDataParams {
type: string;
description?: string;
Expand Down Expand Up @@ -238,17 +243,17 @@ const gatherNames = async (params: ILogParams): Promise<LogDesc[]> => {

for (const id of uniqueIds) {
const item = await collection.findOne({ _id: id });
let name: string = '';
let name: string = `item with id "${id}" has been deleted`;

if (item) {
for (const n of nameFields) {
if (item[n]) {
name = item[n];
}
}

options.push({ [foreignKey]: id, name });
}

options.push({ [foreignKey]: id, name });
}

return options;
Expand Down Expand Up @@ -827,6 +832,25 @@ const gatherPipelineTemplateFieldNames = async (
return options;
};

const gatherUserFieldNames = async (doc: IUserDocument, prevList?: LogDesc[]): Promise<LogDesc[]> => {
let options: LogDesc[] = [];

if (prevList) {
options = prevList;
}

// show only user group names of users for now
options = await gatherNames({
collection: UsersGroups,
idFields: doc.groupIds || [],
foreignKey: 'groupIds',
nameFields: ['name'],
prevList: options,
});

return options;
};

const gatherDescriptions = async (params: IDescriptionParams): Promise<IDescriptions> => {
const { action, type, obj, updatedDocument } = params;

Expand Down Expand Up @@ -1199,6 +1223,16 @@ const gatherDescriptions = async (params: IDescriptionParams): Promise<IDescript
extraDesc = await gatherBoardItemFieldNames(updatedDocument, extraDesc);
}

break;
case MODULE_NAMES.USER:
description = `"${obj.username || obj.email}" has been ${action}`;

extraDesc = await gatherUserFieldNames(obj);

if (updatedDocument) {
extraDesc = await gatherUserFieldNames(updatedDocument, extraDesc);
}

break;
default:
break;
Expand Down
137 changes: 120 additions & 17 deletions src/data/resolvers/mutations/permissions.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,69 @@
import { Permissions, UsersGroups } from '../../../db/models';
import { IPermissionParams, IUserGroup } from '../../../db/models/definitions/permissions';
import * as _ from 'underscore';
import { Permissions, Users, UsersGroups } from '../../../db/models';
import { IPermissionParams, IUserGroup, IUserGroupDocument } from '../../../db/models/definitions/permissions';
import { IUserDocument } from '../../../db/models/definitions/users';
import { MODULE_NAMES } from '../../constants';
import { putCreateLog, putDeleteLog, putUpdateLog } from '../../logUtils';
import { resetPermissionsCache } from '../../permissions/utils';
import { moduleCheckPermission } from '../../permissions/wrappers';
import { IContext } from '../../types';

interface IParams {
memberIds?: string[];
oldUsers: IUserDocument[];
group: IUserGroupDocument;
currentUser: IUserDocument;
}

const writeUserLog = async (params: IParams) => {
const { memberIds = [], oldUsers = [], group, currentUser } = params;

for (const oldUser of oldUsers) {
const exists = memberIds.find(id => id === oldUser._id);

if (!exists) {
const groupIds = oldUser.groupIds ? oldUser.groupIds.filter(item => item !== group._id) : [];
// user has been removed from the group
await putUpdateLog(
{
type: MODULE_NAMES.USER,
object: { _id: oldUser._id, groupIds: oldUser.groupIds },
newData: { groupIds },
description: `User "${oldUser.email}" has been removed from group "${group.name}"`,
updatedDocument: { groupIds },
},
currentUser,
);
}
} // end oldUser loop

for (const memberId of memberIds) {
const exists = oldUsers.find(usr => usr._id === memberId);

// user has been added to the group
if (!exists) {
// already updated user row
const addedUser = await Users.findOne({ _id: memberId });

if (addedUser) {
// previous data was like this
const groupIds = (addedUser.groupIds || []).filter(groupId => groupId !== group._id);

await putUpdateLog(
{
type: MODULE_NAMES.USER,
object: { _id: memberId, groupIds },
newData: { groupIds: addedUser.groupIds },
description: `User "${addedUser.email}" has been added to group ${group.name}`,
updatedDocument: { groupIds: addedUser.groupIds },
},
currentUser,
);
}
}
} // end new user loop
};

const permissionMutations = {
/**
* Create new permission
Expand All @@ -27,7 +85,7 @@ const permissionMutations = {
},
user,
);
} // end for loop
}

await resetPermissionsCache();

Expand All @@ -45,7 +103,7 @@ const permissionMutations = {

for (const perm of permissions) {
await putDeleteLog({ type: MODULE_NAMES.PERMISSION, object: perm }, user);
} // end for loop
}

await resetPermissionsCache();

Expand All @@ -61,21 +119,39 @@ const usersGroupMutations = {
* @return {Promise} newly created group object
*/
async usersGroupsAdd(_root, { memberIds, ...doc }: IUserGroup & { memberIds?: string[] }, { user }: IContext) {
const result = await UsersGroups.createGroup(doc, memberIds);
// users before updating
const oldUsers = await Users.find({ _id: { $in: memberIds || [] } });

const group = await UsersGroups.createGroup(doc, memberIds);

await putCreateLog(
{
type: MODULE_NAMES.USER_GROUP,
object: result,
object: group,
newData: doc,
description: `"${result.name}" has been created`,
description: `"${group.name}" has been created`,
},
user,
);

for (const oldUser of oldUsers) {
const updatedDocument = { groupIds: [...(oldUser.groupIds || []), group._id] };

await putUpdateLog(
{
type: MODULE_NAMES.USER,
object: oldUser,
newData: updatedDocument,
description: `User "${oldUser.email}" has been added to group ${group.name}`,
updatedDocument,
},
user,
);
}

await resetPermissionsCache();

return result;
return group;
},

/**
Expand All @@ -90,17 +166,28 @@ const usersGroupMutations = {
{ user }: IContext,
) {
const group = await UsersGroups.getGroup(_id);
const oldUsers = await Users.find({ groupIds: { $in: [_id] } });
const result = await UsersGroups.updateGroup(_id, doc, memberIds);

await putUpdateLog(
{
type: MODULE_NAMES.USER_GROUP,
object: group,
newData: doc,
description: `"${group.name}" has been edited`,
},
user,
);
// don't write unnecessary log when nothing is changed
if (group.name !== doc.name) {
await putUpdateLog(
{
type: MODULE_NAMES.USER_GROUP,
object: group,
newData: doc,
description: `"${group.name}" has been edited`,
},
user,
);
}

await writeUserLog({
currentUser: user,
memberIds,
oldUsers,
group,
});

await resetPermissionsCache();

Expand All @@ -114,6 +201,7 @@ const usersGroupMutations = {
*/
async usersGroupsRemove(_root, { _id }: { _id: string }, { user }: IContext) {
const group = await UsersGroups.getGroup(_id);
const members = await Users.find({ groupIds: { $in: [group._id] } });
const result = await UsersGroups.removeGroup(_id);

await putDeleteLog(
Expand All @@ -125,6 +213,21 @@ const usersGroupMutations = {
user,
);

for (const member of members) {
const groupIds = member.groupIds ? member.groupIds.filter(id => id !== group._id) : [];

await putUpdateLog(
{
type: MODULE_NAMES.USER,
object: { _id: member._id, groupIds: member.groupIds },
newData: { groupIds },
updatedDocument: { groupIds },
description: `User ${member.email} has been removed from group ${group.name}`,
},
user,
);
}

await resetPermissionsCache();

return result;
Expand Down
5 changes: 5 additions & 0 deletions src/data/resolvers/queries/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { conditionSchema, segmentSchema } from '../../../db/models/definitions/s
import { tagSchema } from '../../../db/models/definitions/tags';
import { taskSchema } from '../../../db/models/definitions/tasks';
import { ticketSchema } from '../../../db/models/definitions/tickets';
import { userSchema } from '../../../db/models/definitions/users';
import { MODULE_NAMES } from '../../constants';
import { fetchLogs, ILogQueryParams } from '../../logUtils';
import { checkPermission } from '../../permissions/wrappers';
Expand Down Expand Up @@ -188,6 +189,10 @@ const LOG_MAPPINGS: ISchemaMap[] = [
name: MODULE_NAMES.SCRIPT,
schemas: [scriptSchema],
},
{
name: MODULE_NAMES.USER,
schemas: [userSchema],
},
];

/**
Expand Down