mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 07:35:09 +00:00
chore: resolve merge conflicts
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import ms from "ms";
|
||||
|
||||
import { ActionProjectType, ProjectMembershipRole, SecretKeyEncoding } from "@app/db/schemas";
|
||||
import { ActionProjectType, ProjectMembershipRole, SecretKeyEncoding, TGroups } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||
@@ -9,6 +9,7 @@ import { decryptAsymmetric, encryptAsymmetric } from "@app/lib/crypto";
|
||||
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
import { isUuidV4 } from "@app/lib/validator";
|
||||
|
||||
import { TGroupDALFactory } from "../../ee/services/group/group-dal";
|
||||
import { TUserGroupMembershipDALFactory } from "../../ee/services/group/user-group-membership-dal";
|
||||
@@ -62,7 +63,7 @@ export const groupProjectServiceFactory = ({
|
||||
actorAuthMethod,
|
||||
roles,
|
||||
projectId,
|
||||
groupId
|
||||
groupIdOrName
|
||||
}: TCreateProjectGroupDTO) => {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
|
||||
@@ -79,13 +80,20 @@ export const groupProjectServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Groups);
|
||||
|
||||
const group = await groupDAL.findOne({ orgId: actorOrgId, id: groupId });
|
||||
if (!group) throw new NotFoundError({ message: `Failed to find group with ID ${groupId}` });
|
||||
let group: TGroups | null = null;
|
||||
if (isUuidV4(groupIdOrName)) {
|
||||
group = await groupDAL.findOne({ orgId: actorOrgId, id: groupIdOrName });
|
||||
}
|
||||
if (!group) {
|
||||
group = await groupDAL.findOne({ orgId: actorOrgId, name: groupIdOrName });
|
||||
}
|
||||
|
||||
if (!group) throw new NotFoundError({ message: `Failed to find group with ID or name ${groupIdOrName}` });
|
||||
|
||||
const existingGroup = await groupProjectDAL.findOne({ groupId: group.id, projectId: project.id });
|
||||
if (existingGroup)
|
||||
throw new BadRequestError({
|
||||
message: `Group with ID ${groupId} already exists in project with id ${project.id}`
|
||||
message: `Group with ID ${group.id} already exists in project with id ${project.id}`
|
||||
});
|
||||
|
||||
for await (const { role: requestedRoleChange } of roles) {
|
||||
@@ -128,7 +136,7 @@ export const groupProjectServiceFactory = ({
|
||||
const projectGroup = await groupProjectDAL.transaction(async (tx) => {
|
||||
const groupProjectMembership = await groupProjectDAL.create(
|
||||
{
|
||||
groupId: group.id,
|
||||
groupId: group!.id,
|
||||
projectId: project.id
|
||||
},
|
||||
tx
|
||||
@@ -163,7 +171,7 @@ export const groupProjectServiceFactory = ({
|
||||
// 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);
|
||||
const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group!.id, project.id, tx);
|
||||
|
||||
if (groupMembers.length) {
|
||||
const ghostUser = await projectDAL.findProjectGhostUser(project.id, tx);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { TProjectPermission } from "@app/lib/types";
|
||||
import { ProjectUserMembershipTemporaryMode } from "../project-membership/project-membership-types";
|
||||
|
||||
export type TCreateProjectGroupDTO = {
|
||||
groupId: string;
|
||||
groupIdOrName: string;
|
||||
roles: (
|
||||
| {
|
||||
role: string;
|
||||
|
||||
@@ -932,15 +932,22 @@ const syncSecretsAWSParameterStore = async ({
|
||||
logger.info(
|
||||
`getIntegrationSecrets: create secret in AWS SSM for [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}]`
|
||||
);
|
||||
await ssm
|
||||
.putParameter({
|
||||
Name: `${integration.path}${key}`,
|
||||
Type: "SecureString",
|
||||
Value: secrets[key].value,
|
||||
...(metadata.kmsKeyId && { KeyId: metadata.kmsKeyId }),
|
||||
Overwrite: true
|
||||
})
|
||||
.promise();
|
||||
|
||||
try {
|
||||
await ssm
|
||||
.putParameter({
|
||||
Name: `${integration.path}${key}`,
|
||||
Type: "SecureString",
|
||||
Value: secrets[key].value,
|
||||
...(metadata.kmsKeyId && { KeyId: metadata.kmsKeyId }),
|
||||
Overwrite: true
|
||||
})
|
||||
.promise();
|
||||
} catch (error) {
|
||||
(error as { secretKey: string }).secretKey = key;
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (metadata.secretAWSTag?.length) {
|
||||
try {
|
||||
await ssm
|
||||
@@ -987,15 +994,20 @@ const syncSecretsAWSParameterStore = async ({
|
||||
|
||||
// we ensure that the KMS key configured in the integration is applied for ALL parameters on AWS
|
||||
if (secrets[key].value && (shouldUpdateKms || awsParameterStoreSecretsObj[key].Value !== secrets[key].value)) {
|
||||
await ssm
|
||||
.putParameter({
|
||||
Name: `${integration.path}${key}`,
|
||||
Type: "SecureString",
|
||||
Value: secrets[key].value,
|
||||
Overwrite: true,
|
||||
...(metadata.kmsKeyId && { KeyId: metadata.kmsKeyId })
|
||||
})
|
||||
.promise();
|
||||
try {
|
||||
await ssm
|
||||
.putParameter({
|
||||
Name: `${integration.path}${key}`,
|
||||
Type: "SecureString",
|
||||
Value: secrets[key].value,
|
||||
Overwrite: true,
|
||||
...(metadata.kmsKeyId && { KeyId: metadata.kmsKeyId })
|
||||
})
|
||||
.promise();
|
||||
} catch (error) {
|
||||
(error as { secretKey: string }).secretKey = key;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (awsParameterStoreSecretsObj[key].Name) {
|
||||
|
||||
@@ -69,6 +69,7 @@ import {
|
||||
TGetOrgMembershipDTO,
|
||||
TInviteUserToOrgDTO,
|
||||
TListProjectMembershipsByOrgMembershipIdDTO,
|
||||
TResendOrgMemberInvitationDTO,
|
||||
TUpdateOrgDTO,
|
||||
TUpdateOrgMembershipDTO,
|
||||
TVerifyUserToOrgDTO
|
||||
@@ -584,6 +585,66 @@ export const orgServiceFactory = ({
|
||||
});
|
||||
return membership;
|
||||
};
|
||||
|
||||
const resendOrgMemberInvitation = async ({
|
||||
orgId,
|
||||
actorId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
membershipId
|
||||
}: TResendOrgMemberInvitationDTO) => {
|
||||
const appCfg = getConfig();
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Member);
|
||||
|
||||
const org = await orgDAL.findOrgById(orgId);
|
||||
|
||||
const [inviteeOrgMembership] = await orgDAL.findMembership({
|
||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId,
|
||||
[`${TableName.OrgMembership}.id` as "id"]: membershipId
|
||||
});
|
||||
|
||||
if (inviteeOrgMembership.status !== OrgMembershipStatus.Invited) {
|
||||
throw new BadRequestError({
|
||||
message: "Organization invitation already accepted"
|
||||
});
|
||||
}
|
||||
|
||||
const token = await tokenService.createTokenForUser({
|
||||
type: TokenType.TOKEN_EMAIL_ORG_INVITATION,
|
||||
userId: inviteeOrgMembership.userId,
|
||||
orgId
|
||||
});
|
||||
|
||||
if (!appCfg.isSmtpConfigured) {
|
||||
return {
|
||||
signupToken: {
|
||||
email: inviteeOrgMembership.email as string,
|
||||
link: `${appCfg.SITE_URL}/signupinvite?token=${token}&to=${inviteeOrgMembership.email}&organization_id=${org?.id}`
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
await smtpService.sendMail({
|
||||
template: SmtpTemplates.OrgInvite,
|
||||
subjectLine: "Infisical organization invitation",
|
||||
recipients: [inviteeOrgMembership.email as string],
|
||||
substitutions: {
|
||||
inviterFirstName: inviteeOrgMembership.firstName,
|
||||
inviterUsername: inviteeOrgMembership.email,
|
||||
organizationName: org?.name,
|
||||
email: inviteeOrgMembership.email,
|
||||
organizationId: org?.id.toString(),
|
||||
token,
|
||||
callback_url: `${appCfg.SITE_URL}/signupinvite`
|
||||
}
|
||||
});
|
||||
|
||||
return { signupToken: undefined };
|
||||
};
|
||||
|
||||
/*
|
||||
* Invite user to organization
|
||||
*/
|
||||
@@ -627,6 +688,7 @@ export const orgServiceFactory = ({
|
||||
}
|
||||
})
|
||||
: [];
|
||||
|
||||
if (projectsToInvite.length !== invitedProjects?.length) {
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Access denied to one or more of the specified projects"
|
||||
@@ -1221,6 +1283,7 @@ export const orgServiceFactory = ({
|
||||
deleteIncidentContact,
|
||||
getOrgGroups,
|
||||
listProjectMembershipsByOrgMembershipId,
|
||||
findOrgBySlug
|
||||
findOrgBySlug,
|
||||
resendOrgMemberInvitation
|
||||
};
|
||||
};
|
||||
|
||||
@@ -35,6 +35,10 @@ export type TInviteUserToOrgDTO = {
|
||||
}[];
|
||||
} & TOrgPermission;
|
||||
|
||||
export type TResendOrgMemberInvitationDTO = {
|
||||
membershipId: string;
|
||||
} & TOrgPermission;
|
||||
|
||||
export type TVerifyUserToOrgDTO = {
|
||||
email: string;
|
||||
orgId: string;
|
||||
|
||||
@@ -29,6 +29,7 @@ import { ActorType } from "../auth/auth-type";
|
||||
import { TCertificateDALFactory } from "../certificate/certificate-dal";
|
||||
import { TCertificateAuthorityDALFactory } from "../certificate-authority/certificate-authority-dal";
|
||||
import { TCertificateTemplateDALFactory } from "../certificate-template/certificate-template-dal";
|
||||
import { TGroupProjectDALFactory } from "../group-project/group-project-dal";
|
||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||
import { TIdentityProjectDALFactory } from "../identity-project/identity-project-dal";
|
||||
import { TIdentityProjectMembershipRoleDALFactory } from "../identity-project/identity-project-membership-role-dal";
|
||||
@@ -100,7 +101,8 @@ type TProjectServiceFactoryDep = {
|
||||
identityProjectDAL: TIdentityProjectDALFactory;
|
||||
identityProjectMembershipRoleDAL: Pick<TIdentityProjectMembershipRoleDALFactory, "create">;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "create" | "findLatestProjectKey" | "delete" | "find" | "insertMany">;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "create" | "findProjectGhostUser" | "findOne">;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "create" | "findProjectGhostUser" | "findOne" | "delete">;
|
||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "delete">;
|
||||
projectSlackConfigDAL: Pick<TProjectSlackConfigDALFactory, "findOne" | "transaction" | "updateById" | "create">;
|
||||
slackIntegrationDAL: Pick<TSlackIntegrationDALFactory, "findById" | "findByIdWithWorkflowIntegrationDetails">;
|
||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "create">;
|
||||
@@ -120,7 +122,7 @@ type TProjectServiceFactoryDep = {
|
||||
orgDAL: Pick<TOrgDALFactory, "findOne">;
|
||||
keyStore: Pick<TKeyStoreFactory, "deleteItem">;
|
||||
projectBotDAL: Pick<TProjectBotDALFactory, "create">;
|
||||
projectRoleDAL: Pick<TProjectRoleDALFactory, "find" | "insertMany">;
|
||||
projectRoleDAL: Pick<TProjectRoleDALFactory, "find" | "insertMany" | "delete">;
|
||||
kmsService: Pick<
|
||||
TKmsServiceFactory,
|
||||
| "updateProjectSecretManagerKmsKey"
|
||||
@@ -169,7 +171,8 @@ export const projectServiceFactory = ({
|
||||
projectBotDAL,
|
||||
projectSlackConfigDAL,
|
||||
slackIntegrationDAL,
|
||||
projectTemplateService
|
||||
projectTemplateService,
|
||||
groupProjectDAL
|
||||
}: TProjectServiceFactoryDep) => {
|
||||
/*
|
||||
* Create workspace. Make user the admin
|
||||
@@ -444,13 +447,32 @@ export const projectServiceFactory = ({
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Project);
|
||||
|
||||
const deletedProject = await projectDAL.transaction(async (tx) => {
|
||||
// delete these so that project custom roles can be deleted in cascade effect
|
||||
// direct deletion of project without these will cause fk error
|
||||
await projectMembershipDAL.delete({ projectId: project.id }, tx);
|
||||
await groupProjectDAL.delete({ projectId: project.id }, tx);
|
||||
const delProject = await projectDAL.deleteById(project.id, tx);
|
||||
const projectGhostUser = await projectMembershipDAL.findProjectGhostUser(project.id, tx).catch(() => null);
|
||||
// akhilmhdh: before removing those kms checking any other project uses it
|
||||
// happened due to project split
|
||||
if (delProject.kmsCertificateKeyId) {
|
||||
await kmsService.deleteInternalKms(delProject.kmsCertificateKeyId, delProject.orgId, tx);
|
||||
const projectsLinkedToForiegnKey = await projectDAL.find(
|
||||
{ kmsCertificateKeyId: delProject.kmsCertificateKeyId },
|
||||
{ tx }
|
||||
);
|
||||
if (!projectsLinkedToForiegnKey.length) {
|
||||
await kmsService.deleteInternalKms(delProject.kmsCertificateKeyId, delProject.orgId, tx);
|
||||
}
|
||||
}
|
||||
|
||||
if (delProject.kmsSecretManagerKeyId) {
|
||||
await kmsService.deleteInternalKms(delProject.kmsSecretManagerKeyId, delProject.orgId, tx);
|
||||
const projectsLinkedToForiegnKey = await projectDAL.find(
|
||||
{ kmsSecretManagerKeyId: delProject.kmsSecretManagerKeyId },
|
||||
{ tx }
|
||||
);
|
||||
if (!projectsLinkedToForiegnKey.length) {
|
||||
await kmsService.deleteInternalKms(delProject.kmsSecretManagerKeyId, delProject.orgId, tx);
|
||||
}
|
||||
}
|
||||
// Delete the org membership for the ghost user if it's found.
|
||||
if (projectGhostUser) {
|
||||
@@ -544,8 +566,10 @@ export const projectServiceFactory = ({
|
||||
const updatedProject = await projectDAL.updateById(project.id, {
|
||||
name: update.name,
|
||||
description: update.description,
|
||||
autoCapitalization: update.autoCapitalization
|
||||
autoCapitalization: update.autoCapitalization,
|
||||
enforceCapitalization: update.autoCapitalization
|
||||
});
|
||||
|
||||
return updatedProject;
|
||||
};
|
||||
|
||||
@@ -567,7 +591,11 @@ export const projectServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
|
||||
|
||||
const updatedProject = await projectDAL.updateById(projectId, { autoCapitalization });
|
||||
const updatedProject = await projectDAL.updateById(projectId, {
|
||||
autoCapitalization,
|
||||
enforceCapitalization: autoCapitalization
|
||||
});
|
||||
|
||||
return updatedProject;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
import bcrypt from "bcrypt";
|
||||
import { z } from "zod";
|
||||
|
||||
import { TSecretSharing } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
||||
import { SecretSharingAccessType } from "@app/lib/types";
|
||||
import { isUuidV4 } from "@app/lib/validator";
|
||||
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import { TOrgDALFactory } from "../org/org-dal";
|
||||
@@ -28,8 +28,6 @@ type TSecretSharingServiceFactoryDep = {
|
||||
|
||||
export type TSecretSharingServiceFactory = ReturnType<typeof secretSharingServiceFactory>;
|
||||
|
||||
const isUuidV4 = (uuid: string) => z.string().uuid().safeParse(uuid).success;
|
||||
|
||||
export const secretSharingServiceFactory = ({
|
||||
permissionService,
|
||||
secretSharingDAL,
|
||||
|
||||
@@ -88,7 +88,7 @@ type TSecretServiceFactoryDep = {
|
||||
secretDAL: TSecretDALFactory;
|
||||
secretTagDAL: TSecretTagDALFactory;
|
||||
secretVersionDAL: TSecretVersionDALFactory;
|
||||
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus" | "findProjectBySlug">;
|
||||
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus" | "findProjectBySlug" | "findById">;
|
||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
||||
folderDAL: Pick<
|
||||
TSecretFolderDALFactory,
|
||||
@@ -1466,6 +1466,16 @@ export const secretServiceFactory = ({
|
||||
secretMetadata
|
||||
}: TCreateSecretRawDTO) => {
|
||||
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
||||
const project = await projectDAL.findById(projectId);
|
||||
if (project.enforceCapitalization) {
|
||||
if (secretName !== secretName.toUpperCase()) {
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Secret name must be in UPPERCASE per project requirements. You can disable this requirement in project settings."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const policy =
|
||||
actor === ActorType.USER && type === SecretType.Shared
|
||||
? await secretApprovalPolicyService.getSecretApprovalPolicy(projectId, environment, secretPath)
|
||||
@@ -1609,6 +1619,16 @@ export const secretServiceFactory = ({
|
||||
secretMetadata
|
||||
}: TUpdateSecretRawDTO) => {
|
||||
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
||||
const project = await projectDAL.findById(projectId);
|
||||
if (project.enforceCapitalization) {
|
||||
if (newSecretName && newSecretName !== newSecretName.toUpperCase()) {
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Secret name must be in UPPERCASE per project requirements. You can disable this requirement in project settings."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const policy =
|
||||
actor === ActorType.USER && type === SecretType.Shared
|
||||
? await secretApprovalPolicyService.getSecretApprovalPolicy(projectId, environment, secretPath)
|
||||
@@ -1858,7 +1878,23 @@ export const secretServiceFactory = ({
|
||||
actor === ActorType.USER
|
||||
? await secretApprovalPolicyService.getSecretApprovalPolicy(projectId, environment, secretPath)
|
||||
: undefined;
|
||||
|
||||
if (shouldUseSecretV2Bridge) {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
if (project.enforceCapitalization) {
|
||||
const caseViolatingSecretKeys = inputSecrets
|
||||
.filter((sec) => sec.secretKey !== sec.secretKey.toUpperCase())
|
||||
.map((sec) => sec.secretKey);
|
||||
|
||||
if (caseViolatingSecretKeys.length) {
|
||||
throw new BadRequestError({
|
||||
message: `Secret names must be in UPPERCASE per project requirements: ${caseViolatingSecretKeys.join(
|
||||
", "
|
||||
)}. You can disable this requirement in project settings`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (policy) {
|
||||
const approval = await secretApprovalRequestService.generateSecretApprovalRequestV2Bridge({
|
||||
policy,
|
||||
@@ -1987,6 +2023,21 @@ export const secretServiceFactory = ({
|
||||
? await secretApprovalPolicyService.getSecretApprovalPolicy(projectId, environment, secretPath)
|
||||
: undefined;
|
||||
if (shouldUseSecretV2Bridge) {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
if (project.enforceCapitalization) {
|
||||
const caseViolatingSecretKeys = inputSecrets
|
||||
.filter((sec) => sec.newSecretName && sec.newSecretName !== sec.newSecretName.toUpperCase())
|
||||
.map((sec) => sec.newSecretName);
|
||||
|
||||
if (caseViolatingSecretKeys.length) {
|
||||
throw new BadRequestError({
|
||||
message: `Secret names must be in UPPERCASE per project requirements: ${caseViolatingSecretKeys.join(
|
||||
", "
|
||||
)}. You can disable this requirement in project settings`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (policy) {
|
||||
const approval = await secretApprovalRequestService.generateSecretApprovalRequestV2Bridge({
|
||||
policy,
|
||||
|
||||
Reference in New Issue
Block a user