mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-09 05:28:23 +00:00
Patch adding groups to project for invited users, add transactions for adding/removing groups to/from projects
This commit is contained in:
@@ -61,7 +61,7 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return list of users that are part of the group with id [groupId]
|
* Return list of completed/accepted users that are part of the group with id [groupId]
|
||||||
* that have not yet been added individually to project with id [projectId].
|
* that have not yet been added individually to project with id [projectId].
|
||||||
*
|
*
|
||||||
* Note: Filters out users that are part of other groups in the project.
|
* Note: Filters out users that are part of other groups in the project.
|
||||||
@@ -69,18 +69,19 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
* @param projectId
|
* @param projectId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const findGroupMembersNotInProject = async (groupId: string, projectId: string) => {
|
const findGroupMembersNotInProject = async (groupId: string, projectId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
// get list of groups in the project with id [projectId]
|
// get list of groups in the project with id [projectId]
|
||||||
// that that are not the group with id [groupId]
|
// that that are not the group with id [groupId]
|
||||||
const groups: string[] = await db(TableName.GroupProjectMembership)
|
const groups: string[] = await (tx || db)(TableName.GroupProjectMembership)
|
||||||
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
||||||
.whereNot(`${TableName.GroupProjectMembership}.groupId`, groupId)
|
.whereNot(`${TableName.GroupProjectMembership}.groupId`, groupId)
|
||||||
.pluck(`${TableName.GroupProjectMembership}.groupId`);
|
.pluck(`${TableName.GroupProjectMembership}.groupId`);
|
||||||
|
|
||||||
// main query
|
// main query
|
||||||
const members = await db(TableName.UserGroupMembership)
|
const members = await (tx || db)(TableName.UserGroupMembership)
|
||||||
.where(`${TableName.UserGroupMembership}.groupId`, groupId)
|
.where(`${TableName.UserGroupMembership}.groupId`, groupId)
|
||||||
|
.where(`${TableName.UserGroupMembership}.isPending`, false)
|
||||||
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||||
.leftJoin(TableName.ProjectMembership, function () {
|
.leftJoin(TableName.ProjectMembership, function () {
|
||||||
this.on(`${TableName.Users}.id`, "=", `${TableName.ProjectMembership}.userId`).andOn(
|
this.on(`${TableName.Users}.id`, "=", `${TableName.ProjectMembership}.userId`).andOn(
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ type TGroupProjectServiceFactoryDep = {
|
|||||||
TGroupProjectMembershipRoleDALFactory,
|
TGroupProjectMembershipRoleDALFactory,
|
||||||
"create" | "transaction" | "insertMany" | "delete"
|
"create" | "transaction" | "insertMany" | "delete"
|
||||||
>;
|
>;
|
||||||
userGroupMembershipDAL: TUserGroupMembershipDALFactory;
|
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "findGroupMembersNotInProject">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "findProjectGhostUser">;
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "findProjectGhostUser">;
|
||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "findLatestProjectKey" | "delete" | "insertMany" | "transaction">;
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "findLatestProjectKey" | "delete" | "insertMany" | "transaction">;
|
||||||
projectRoleDAL: Pick<TProjectRoleDALFactory, "find">;
|
projectRoleDAL: Pick<TProjectRoleDALFactory, "find">;
|
||||||
@@ -116,68 +116,69 @@ export const groupProjectServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// share project key with users in group that have not
|
||||||
|
// individually been added to the project and that are not part of
|
||||||
|
// other groups that are in the project
|
||||||
|
const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group.id, project.id, tx);
|
||||||
|
|
||||||
|
if (groupMembers.length) {
|
||||||
|
const ghostUser = await projectDAL.findProjectGhostUser(project.id, tx);
|
||||||
|
|
||||||
|
if (!ghostUser) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to find sudo user"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const ghostUserLatestKey = await projectKeyDAL.findLatestProjectKey(ghostUser.id, project.id, tx);
|
||||||
|
|
||||||
|
if (!ghostUserLatestKey) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to find sudo user latest key"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const bot = await projectBotDAL.findOne({ projectId: project.id }, tx);
|
||||||
|
|
||||||
|
if (!bot) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to find bot"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const botPrivateKey = infisicalSymmetricDecrypt({
|
||||||
|
keyEncoding: bot.keyEncoding as SecretKeyEncoding,
|
||||||
|
iv: bot.iv,
|
||||||
|
tag: bot.tag,
|
||||||
|
ciphertext: bot.encryptedPrivateKey
|
||||||
|
});
|
||||||
|
|
||||||
|
const plaintextProjectKey = decryptAsymmetric({
|
||||||
|
ciphertext: ghostUserLatestKey.encryptedKey,
|
||||||
|
nonce: ghostUserLatestKey.nonce,
|
||||||
|
publicKey: ghostUserLatestKey.sender.publicKey,
|
||||||
|
privateKey: botPrivateKey
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectKeyData = groupMembers.map(({ user: { publicKey, id } }) => {
|
||||||
|
const { ciphertext: encryptedKey, nonce } = encryptAsymmetric(plaintextProjectKey, publicKey, botPrivateKey);
|
||||||
|
|
||||||
|
return {
|
||||||
|
encryptedKey,
|
||||||
|
nonce,
|
||||||
|
senderId: ghostUser.id,
|
||||||
|
receiverId: id,
|
||||||
|
projectId: project.id
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
await projectKeyDAL.insertMany(projectKeyData, tx);
|
||||||
|
}
|
||||||
|
|
||||||
return groupProjectMembership;
|
return groupProjectMembership;
|
||||||
});
|
});
|
||||||
|
|
||||||
// share project key with users in group that have not
|
|
||||||
// individually been added to the project and that are not part of
|
|
||||||
// other groups that are in the project
|
|
||||||
const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group.id, project.id);
|
|
||||||
|
|
||||||
if (groupMembers.length) {
|
|
||||||
const ghostUser = await projectDAL.findProjectGhostUser(project.id);
|
|
||||||
|
|
||||||
if (!ghostUser) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: "Failed to find sudo user"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const ghostUserLatestKey = await projectKeyDAL.findLatestProjectKey(ghostUser.id, project.id);
|
|
||||||
|
|
||||||
if (!ghostUserLatestKey) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: "Failed to find sudo user latest key"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const bot = await projectBotDAL.findOne({ projectId: project.id });
|
|
||||||
|
|
||||||
if (!bot) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: "Failed to find bot"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const botPrivateKey = infisicalSymmetricDecrypt({
|
|
||||||
keyEncoding: bot.keyEncoding as SecretKeyEncoding,
|
|
||||||
iv: bot.iv,
|
|
||||||
tag: bot.tag,
|
|
||||||
ciphertext: bot.encryptedPrivateKey
|
|
||||||
});
|
|
||||||
|
|
||||||
const plaintextProjectKey = decryptAsymmetric({
|
|
||||||
ciphertext: ghostUserLatestKey.encryptedKey,
|
|
||||||
nonce: ghostUserLatestKey.nonce,
|
|
||||||
publicKey: ghostUserLatestKey.sender.publicKey,
|
|
||||||
privateKey: botPrivateKey
|
|
||||||
});
|
|
||||||
|
|
||||||
const projectKeyData = groupMembers.map(({ user: { publicKey, id } }) => {
|
|
||||||
const { ciphertext: encryptedKey, nonce } = encryptAsymmetric(plaintextProjectKey, publicKey, botPrivateKey);
|
|
||||||
|
|
||||||
return {
|
|
||||||
encryptedKey,
|
|
||||||
nonce,
|
|
||||||
senderId: ghostUser.id,
|
|
||||||
receiverId: id,
|
|
||||||
projectId: project.id
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
await projectKeyDAL.insertMany(projectKeyData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return projectGroup;
|
return projectGroup;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -287,20 +288,26 @@ export const groupProjectServiceFactory = ({
|
|||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Groups);
|
||||||
|
|
||||||
const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group.id, project.id);
|
const deletedProjectGroup = await groupProjectDAL.transaction(async (tx) => {
|
||||||
|
const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group.id, project.id, tx);
|
||||||
|
|
||||||
if (groupMembers.length) {
|
if (groupMembers.length) {
|
||||||
await projectKeyDAL.delete({
|
await projectKeyDAL.delete(
|
||||||
projectId: project.id,
|
{
|
||||||
$in: {
|
projectId: project.id,
|
||||||
receiverId: groupMembers.map(({ user: { id } }) => id)
|
$in: {
|
||||||
}
|
receiverId: groupMembers.map(({ user: { id } }) => id)
|
||||||
});
|
}
|
||||||
}
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const [deletedGroup] = await groupProjectDAL.delete({ groupId: group.id, projectId: project.id });
|
const [projectGroup] = await groupProjectDAL.delete({ groupId: group.id, projectId: project.id }, tx);
|
||||||
|
return projectGroup;
|
||||||
|
});
|
||||||
|
|
||||||
return deletedGroup;
|
return deletedProjectGroup;
|
||||||
};
|
};
|
||||||
|
|
||||||
const listGroupsInProject = async ({
|
const listGroupsInProject = async ({
|
||||||
|
|||||||
Reference in New Issue
Block a user