Skip to content

Commit

Permalink
Merge pull request #2466 from ideafast/feat/trpc-role
Browse files Browse the repository at this point in the history
Feat/trpc role
  • Loading branch information
wsy19961129 authored Jul 12, 2024
2 parents 1c1e4ca + 2325f49 commit 23b53b0
Show file tree
Hide file tree
Showing 6 changed files with 674 additions and 20 deletions.
18 changes: 9 additions & 9 deletions packages/itmat-cores/src/trpcCore/dataCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class TRPCDataCore {
enumCoreErrors.NOT_LOGGED_IN
);
}
const roles = await this.permissionCore.getRolesOfUser(requester, studyId);
const roles = await this.permissionCore.getRolesOfUser(requester, requester.id, studyId);
if (roles.length === 0) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
Expand Down Expand Up @@ -163,7 +163,7 @@ export class TRPCDataCore {
);
}

const roles = await this.permissionCore.getRolesOfUser(requester, fieldInput.studyId);
const roles = await this.permissionCore.getRolesOfUser(requester, requester.id, fieldInput.studyId);
if (roles.length === 0) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
Expand Down Expand Up @@ -279,7 +279,7 @@ export class TRPCDataCore {
);
}

const roles = await this.permissionCore.getRolesOfUser(requester, fieldInput.studyId);
const roles = await this.permissionCore.getRolesOfUser(requester, requester.id, fieldInput.studyId);
if (roles.length === 0) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
Expand Down Expand Up @@ -395,7 +395,7 @@ export class TRPCDataCore {
);
}

const roles = await this.permissionCore.getRolesOfUser(requester, studyId);
const roles = await this.permissionCore.getRolesOfUser(requester, requester.id, studyId);
if (roles.length === 0) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
Expand Down Expand Up @@ -745,7 +745,7 @@ export class TRPCDataCore {
enumCoreErrors.NOT_LOGGED_IN
);
}
const roles = (await this.permissionCore.getRolesOfUser(requester, studyId));
const roles = (await this.permissionCore.getRolesOfUser(requester, requester.id, studyId));
if (roles.length === 0) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
Expand Down Expand Up @@ -864,7 +864,7 @@ export class TRPCDataCore {
enumCoreErrors.NOT_LOGGED_IN
);
}
const roles = (await this.permissionCore.getRolesOfUser(requester, studyId));
const roles = (await this.permissionCore.getRolesOfUser(requester, requester.id, studyId));
if (roles.length === 0) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
Expand Down Expand Up @@ -957,7 +957,7 @@ export class TRPCDataCore {
enumCoreErrors.NOT_LOGGED_IN
);
}
const roles = (await this.permissionCore.getRolesOfUser(requester, studyId));
const roles = (await this.permissionCore.getRolesOfUser(requester, requester.id, studyId));
if (roles.length === 0) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
Expand Down Expand Up @@ -1168,7 +1168,7 @@ export class TRPCDataCore {
);
}

const roles = await this.permissionCore.getRolesOfUser(requester, studyId);
const roles = await this.permissionCore.getRolesOfUser(requester, requester.id, studyId);
if (roles.length === 0) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
Expand Down Expand Up @@ -1224,7 +1224,7 @@ export class TRPCDataCore {
);
}

const roles = await this.permissionCore.getRolesOfUser(requester, studyId);
const roles = await this.permissionCore.getRolesOfUser(requester, requester.id, studyId);
if (requester.type !== enumUserTypes.ADMIN && roles.length === 0) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
Expand Down
209 changes: 203 additions & 6 deletions packages/itmat-cores/src/trpcCore/permissionCore.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { IData, IField, IUserWithoutToken, enumDataAtomicPermissions, permissionString } from '@itmat-broker/itmat-types';
import { CoreError, IData, IDataPermission, IField, IRole, IUserWithoutToken, enumCoreErrors, enumDataAtomicPermissions, enumStudyRoles, enumUserTypes, permissionString } from '@itmat-broker/itmat-types';
import { DBType } from '../database/database';
import { v4 as uuid } from 'uuid';
import { makeGenericReponse } from '../utils';

export class TRPCPermissionCore {
db: DBType;
Expand All @@ -12,11 +14,62 @@ export class TRPCPermissionCore {
*
* @param user
* @param studyId
* @returns
*
* @returns - IRole[]
*/
public async getRolesOfUser(user: IUserWithoutToken, studyId?: string) {
return studyId ? await this.db.collections.roles_collection.find({ 'studyId': studyId, 'users': user.id, 'life.deletedTime': null }).toArray() :
await this.db.collections.roles_collection.find({ 'users': user.id, 'life.deletedTime': null }).toArray();
public async getRolesOfUser(requester: IUserWithoutToken | undefined, userId: string, studyId?: string) {
if (!requester) {
throw new CoreError(
enumCoreErrors.NOT_LOGGED_IN,
enumCoreErrors.NOT_LOGGED_IN
);
}
// if studyId is provided, only admins, study managers and user him/herself can see the roles
// if studyId is not provided, only admins and user him/herself can see the roles
if (studyId) {
const requesterRoles = await this.db.collections.roles_collection.find({ 'studyId': studyId, 'users': requester.id, 'life.deletedTime': null }).toArray();
if (requester.type !== enumUserTypes.ADMIN && requesterRoles.every(role => role.studyRole !== enumStudyRoles.STUDY_MANAGER) && requester.id !== userId) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
enumCoreErrors.NO_PERMISSION_ERROR
);
}
return await this.db.collections.roles_collection.find({ 'studyId': studyId, 'users': userId, 'life.deletedTime': null }).toArray();
} else {
if (requester.type !== enumUserTypes.ADMIN && requester.id !== userId) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
enumCoreErrors.NO_PERMISSION_ERROR
);
}
return await this.db.collections.roles_collection.find({ 'users': userId, 'life.deletedTime': null }).toArray();
}
}

/**
* Get the roles of a study.
*
* @param requester - The requester.
* @param studyId - The id of the study.
*
* @returns - IRole[]
*/
public async getRolesOfStudy(requester: IUserWithoutToken | undefined, studyId: string) {
if (!requester) {
throw new CoreError(
enumCoreErrors.NOT_LOGGED_IN,
enumCoreErrors.NOT_LOGGED_IN
);
}
const roles = await this.getRolesOfUser(requester, requester.id, studyId);
if (requester.type !== enumUserTypes.ADMIN && roles.every(role => role.studyRole !== enumStudyRoles.STUDY_MANAGER)) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
enumCoreErrors.NO_PERMISSION_ERROR
);
}

return await this.db.collections.roles_collection.find({ 'studyId': studyId, 'life.deletedTime': null }).toArray();
}

/**
Expand All @@ -30,7 +83,7 @@ export class TRPCPermissionCore {
* @returns boolean
*/
public async checkFieldOrDataPermission(user: IUserWithoutToken, studyId: string, entry: Partial<IField> | Partial<IData>, permission: enumDataAtomicPermissions) {
const roles = await this.getRolesOfUser(user, studyId);
const roles = await this.getRolesOfUser(user, user.id, studyId);
for (const role of roles) {
const dataPermissions = role.dataPermissions;
for (const dataPermission of dataPermissions) {
Expand All @@ -57,4 +110,148 @@ export class TRPCPermissionCore {
}
return true;
}

/**
* Create a new role.
*
* @param requester - The requester.
* @param studyId - The id of the study.
* @param name - The name of the role.
* @param description - The description of the role.
* @param dataPermissions - The data permissions of the role.
* @param studyRole - The role of the study.
* @param users - The users of the role.
* @returns IRole
*/
public async createStudyRole(requester: IUserWithoutToken | undefined, studyId: string, name: string, description?: string, dataPermissions?: IDataPermission[], studyRole?: enumStudyRoles, users?: string[]) {
if (!requester) {
throw new CoreError(
enumCoreErrors.NOT_LOGGED_IN,
enumCoreErrors.NOT_LOGGED_IN
);
}

const roles = await this.getRolesOfUser(requester, requester.id, studyId);
if (requester.type !== enumUserTypes.ADMIN && roles.every(role => role.studyRole !== enumStudyRoles.STUDY_MANAGER)) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
enumCoreErrors.NO_PERMISSION_ERROR
);
}

const newRole: IRole = {
id: uuid(),
studyId: studyId,
name: name,
description: description,
dataPermissions: dataPermissions ?? [{
fields: [],
dataProperties: {},
includeUnVersioned: false,
permission: 0
}],
studyRole: studyRole ?? enumStudyRoles.STUDY_USER,
users: users ?? [],
life: {
createdTime: Date.now(),
createdUser: requester.id,
deletedTime: null,
deletedUser: null
},
metadata: {}
};

await this.db.collections.roles_collection.insertOne(newRole);
return newRole;
}

/**
* Edit a role.
*
* @param requester - The requester.
* @param roleId - The id of the role.
* @param name - The name of the role.
* @param description - The description of the role.
* @param dataPermissions - The data permissions of the role.
* @param studyRole - The role of the study.
* @param users - The users of the role.
*
* @returns IRole
*/
public async editStudyRole(requester: IUserWithoutToken | undefined, roleId: string, name?: string, description?: string, dataPermissions?: IDataPermission[], studyRole?: enumStudyRoles, users?: string[]) {
if (!requester) {
throw new CoreError(
enumCoreErrors.NOT_LOGGED_IN,
enumCoreErrors.NOT_LOGGED_IN
);
}

const role = await this.db.collections.roles_collection.findOne({ 'id': roleId, 'life.deletedTime': null });
if (!role) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
enumCoreErrors.NO_PERMISSION_ERROR
);
}
const roles = await this.getRolesOfUser(requester, requester.id, role.studyId);
if (requester.type !== enumUserTypes.ADMIN && roles.every(role => role.studyRole !== enumStudyRoles.STUDY_MANAGER)) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
enumCoreErrors.NO_PERMISSION_ERROR
);
}

const res = await this.db.collections.roles_collection.findOneAndUpdate({ id: roleId }, {
$set: {
name: name ?? role.name,
description: description ?? role.description,
dataPermissions: dataPermissions ?? role.dataPermissions,
studyRole: studyRole ?? role.studyRole,
users: users ?? role.users
}
}, {
returnDocument: 'after'
});
return res;
}

/**
* Delete a role.
*
* @param requester - The requester.
* @param roleId - The id of the role.
* @returns - IGenericResponse
*/
public async deleteStudyRole(requester: IUserWithoutToken | undefined, roleId: string) {
if (!requester) {
throw new CoreError(
enumCoreErrors.NOT_LOGGED_IN,
enumCoreErrors.NOT_LOGGED_IN
);
}

const role = await this.db.collections.roles_collection.findOne({ 'id': roleId, 'life.deletedTime': null });
if (!role) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
enumCoreErrors.NO_PERMISSION_ERROR
);
}
const roles = await this.getRolesOfUser(requester, requester.id, role.studyId);
if (requester.type !== enumUserTypes.ADMIN && roles.every(role => role.studyRole !== enumStudyRoles.STUDY_MANAGER)) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
enumCoreErrors.NO_PERMISSION_ERROR
);
}

await this.db.collections.roles_collection.findOneAndUpdate({ id: roleId }, {
$set: {
'life.deletedTime': Date.now(),
'life.deletedUser': requester.id
}
});

return makeGenericReponse(roleId, true, undefined, 'Role deleted successfully.');
}
}
8 changes: 4 additions & 4 deletions packages/itmat-cores/src/trpcCore/studyCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class TRPCStudyCore {
return studyId ? await this.db.collections.studies_collection.find({ 'id': studyId, 'life.deletedTime': null }).toArray() :
await this.db.collections.studies_collection.find({ 'life.deletedTime': null }).toArray();
}
const roleStudyIds = (await this.permissionCore.getRolesOfUser(requester)).map(role => role.studyId);
const roleStudyIds = (await this.permissionCore.getRolesOfUser(requester, requester.id)).map(role => role.studyId);

const query: Filter<IStudy> = { 'life.deletedTime': null };
if (studyId) {
Expand Down Expand Up @@ -179,7 +179,7 @@ export class TRPCStudyCore {
enumCoreErrors.NOT_LOGGED_IN
);
}
const roleStudyRoles: enumStudyRoles[] = (await this.permissionCore.getRolesOfUser(requester, studyId)).map(role => role.studyRole);
const roleStudyRoles: enumStudyRoles[] = (await this.permissionCore.getRolesOfUser(requester, requester.id, studyId)).map(role => role.studyRole);
if (requester.type !== enumUserTypes.ADMIN && !roleStudyRoles.includes(enumStudyRoles.STUDY_MANAGER)) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
Expand Down Expand Up @@ -307,7 +307,7 @@ export class TRPCStudyCore {
);
}

const roleStudyRoles: enumStudyRoles[] = (await this.permissionCore.getRolesOfUser(requester)).map(role => role.studyRole);
const roleStudyRoles: enumStudyRoles[] = (await this.permissionCore.getRolesOfUser(requester, requester.id)).map(role => role.studyRole);
if (requester.type !== enumUserTypes.ADMIN && !roleStudyRoles.includes(enumStudyRoles.STUDY_MANAGER)) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
Expand Down Expand Up @@ -448,7 +448,7 @@ export class TRPCStudyCore {
}

/* check privileges */
const roleStudyRoles: enumStudyRoles[] = (await this.permissionCore.getRolesOfUser(requester)).map(role => role.studyRole);
const roleStudyRoles: enumStudyRoles[] = (await this.permissionCore.getRolesOfUser(requester, requester.id)).map(role => role.studyRole);
if (requester.type !== enumUserTypes.ADMIN && !roleStudyRoles.includes(enumStudyRoles.STUDY_MANAGER)) {
throw new CoreError(
enumCoreErrors.NO_PERMISSION_ERROR,
Expand Down
Loading

0 comments on commit 23b53b0

Please sign in to comment.