mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 23:52:09 +00:00
feat: changed line length to 120
This commit is contained in:
@@ -45,8 +45,7 @@ export const apiKeyServiceFactory = ({ apiKeyDAL, userDAL }: TApiKeyServiceFacto
|
||||
|
||||
const deleteApiKey = async (userId: string, apiKeyId: string) => {
|
||||
const [apiKeyData] = await apiKeyDAL.delete({ id: apiKeyId, userId });
|
||||
if (!apiKeyData)
|
||||
throw new BadRequestError({ message: "Failed to find api key", name: "delete api key" });
|
||||
if (!apiKeyData) throw new BadRequestError({ message: "Failed to find api key", name: "delete api key" });
|
||||
return formatApiKey(apiKeyData);
|
||||
};
|
||||
|
||||
|
||||
@@ -12,9 +12,7 @@ export type TTokenDALFactory = ReturnType<typeof tokenDALFactory>;
|
||||
export const tokenDALFactory = (db: TDbClient) => {
|
||||
const authOrm = ormify(db, TableName.AuthTokens);
|
||||
|
||||
const findOneTokenSession = async (
|
||||
filter: Partial<TAuthTokenSessions>
|
||||
): Promise<TAuthTokenSessions | undefined> => {
|
||||
const findOneTokenSession = async (filter: Partial<TAuthTokenSessions>): Promise<TAuthTokenSessions | undefined> => {
|
||||
try {
|
||||
const doc = await db(TableName.AuthTokenSession).where(filter).first();
|
||||
return doc;
|
||||
@@ -29,20 +27,14 @@ export const tokenDALFactory = (db: TDbClient) => {
|
||||
orgId
|
||||
}: TDeleteTokenForUserDALDTO): Promise<TAuthTokens[] | undefined> => {
|
||||
try {
|
||||
const doc = await db(TableName.AuthTokens)
|
||||
.where({ userId, type, orgId })
|
||||
.delete()
|
||||
.returning("*");
|
||||
const doc = await db(TableName.AuthTokens).where({ userId, type, orgId }).delete().returning("*");
|
||||
return doc;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "DeleteTokenForUser" });
|
||||
}
|
||||
};
|
||||
|
||||
const decrementTriesField = async ({
|
||||
userId,
|
||||
type
|
||||
}: TDeleteTokenForUserDALDTO): Promise<void> => {
|
||||
const decrementTriesField = async ({ userId, type }: TDeleteTokenForUserDALDTO): Promise<void> => {
|
||||
try {
|
||||
await db(TableName.AuthTokens).where({ userId, type }).decrement("triesLeft", 1);
|
||||
} catch (error) {
|
||||
@@ -99,10 +91,7 @@ export const tokenDALFactory = (db: TDbClient) => {
|
||||
|
||||
const deleteTokenSession = async (filter: Partial<TAuthTokenSessions>, tx?: Knex) => {
|
||||
try {
|
||||
const sessions = await (tx || db)(TableName.AuthTokenSession)
|
||||
.where(filter)
|
||||
.del()
|
||||
.returning("*");
|
||||
const sessions = await (tx || db)(TableName.AuthTokenSession).where(filter).del().returning("*");
|
||||
return sessions;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ name: "Delete token session", error });
|
||||
|
||||
@@ -9,12 +9,7 @@ import { UnauthorizedError } from "@app/lib/errors";
|
||||
import { AuthModeJwtTokenPayload } from "../auth/auth-type";
|
||||
import { TUserDALFactory } from "../user/user-dal";
|
||||
import { TTokenDALFactory } from "./auth-token-dal";
|
||||
import {
|
||||
TCreateTokenForUserDTO,
|
||||
TIssueAuthTokenDTO,
|
||||
TokenType,
|
||||
TValidateTokenForUserDTO
|
||||
} from "./auth-token-types";
|
||||
import { TCreateTokenForUserDTO, TIssueAuthTokenDTO, TokenType, TValidateTokenForUserDTO } from "./auth-token-types";
|
||||
|
||||
type TAuthTokenServiceFactoryDep = {
|
||||
tokenDAL: TTokenDALFactory;
|
||||
@@ -125,14 +120,10 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL }: TAuthTokenServiceFact
|
||||
return session;
|
||||
};
|
||||
|
||||
const clearTokenSessionById = async (
|
||||
userId: string,
|
||||
sessionId: string
|
||||
): Promise<TAuthTokenSessions | undefined> =>
|
||||
const clearTokenSessionById = async (userId: string, sessionId: string): Promise<TAuthTokenSessions | undefined> =>
|
||||
tokenDAL.incrementTokenSessionVersion(userId, sessionId);
|
||||
|
||||
const getUserTokenSessionById = async (id: string, userId: string) =>
|
||||
tokenDAL.findOneTokenSession({ id, userId });
|
||||
const getUserTokenSessionById = async (id: string, userId: string) => tokenDAL.findOneTokenSession({ id, userId });
|
||||
|
||||
const getTokenSessionByUser = async (userId: string) => tokenDAL.findTokenSessions({ userId });
|
||||
|
||||
@@ -145,8 +136,7 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL }: TAuthTokenServiceFact
|
||||
userId: token.userId
|
||||
});
|
||||
if (!session) throw new UnauthorizedError({ name: "Session not found" });
|
||||
if (token.accessVersion !== session.accessVersion)
|
||||
throw new UnauthorizedError({ name: "Stale session" });
|
||||
if (token.accessVersion !== session.accessVersion) throw new UnauthorizedError({ name: "Stale session" });
|
||||
|
||||
const user = await userDAL.findById(session.userId);
|
||||
if (!user || !user.isAccepted) throw new UnauthorizedError({ name: "Token user not found" });
|
||||
|
||||
@@ -3,19 +3,12 @@ import jwt from "jsonwebtoken";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
|
||||
import {
|
||||
AuthModeProviderJwtTokenPayload,
|
||||
AuthModeProviderSignUpTokenPayload,
|
||||
AuthTokenType
|
||||
} from "./auth-type";
|
||||
import { AuthModeProviderJwtTokenPayload, AuthModeProviderSignUpTokenPayload, AuthTokenType } from "./auth-type";
|
||||
|
||||
export const validateProviderAuthToken = (providerToken: string, email: string) => {
|
||||
if (!providerToken) throw new UnauthorizedError();
|
||||
const appCfg = getConfig();
|
||||
const decodedToken = jwt.verify(
|
||||
providerToken,
|
||||
appCfg.AUTH_SECRET
|
||||
) as AuthModeProviderJwtTokenPayload;
|
||||
const decodedToken = jwt.verify(providerToken, appCfg.AUTH_SECRET) as AuthModeProviderJwtTokenPayload;
|
||||
|
||||
if (decodedToken.authTokenType !== AuthTokenType.PROVIDER_TOKEN) throw new UnauthorizedError();
|
||||
if (decodedToken.email !== email) throw new Error("Invalid auth credentials");
|
||||
@@ -23,10 +16,7 @@ export const validateProviderAuthToken = (providerToken: string, email: string)
|
||||
|
||||
export const validateSignUpAuthorization = (token: string, userId: string, validate = true) => {
|
||||
const appCfg = getConfig();
|
||||
const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>token?.split(" ", 2) ?? [
|
||||
null,
|
||||
null
|
||||
];
|
||||
const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>token?.split(" ", 2) ?? [null, null];
|
||||
if (AUTH_TOKEN_TYPE === null) {
|
||||
throw new BadRequestError({ message: "Missing Authorization Header in the request header." });
|
||||
}
|
||||
@@ -41,10 +31,7 @@ export const validateSignUpAuthorization = (token: string, userId: string, valid
|
||||
});
|
||||
}
|
||||
|
||||
const decodedToken = jwt.verify(
|
||||
AUTH_TOKEN_VALUE,
|
||||
appCfg.AUTH_SECRET
|
||||
) as AuthModeProviderSignUpTokenPayload;
|
||||
const decodedToken = jwt.verify(AUTH_TOKEN_VALUE, appCfg.AUTH_SECRET) as AuthModeProviderSignUpTokenPayload;
|
||||
if (!validate) return decodedToken;
|
||||
|
||||
if (decodedToken.authTokenType !== AuthTokenType.SIGNUP_TOKEN) throw new UnauthorizedError();
|
||||
|
||||
@@ -25,11 +25,7 @@ type TAuthLoginServiceFactoryDep = {
|
||||
};
|
||||
|
||||
export type TAuthLoginFactory = ReturnType<typeof authLoginServiceFactory>;
|
||||
export const authLoginServiceFactory = ({
|
||||
userDAL,
|
||||
tokenService,
|
||||
smtpService
|
||||
}: TAuthLoginServiceFactoryDep) => {
|
||||
export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }: TAuthLoginServiceFactoryDep) => {
|
||||
/*
|
||||
* Private
|
||||
* Not exported. This is to update user device list
|
||||
@@ -37,9 +33,7 @@ export const authLoginServiceFactory = ({
|
||||
*/
|
||||
const updateUserDeviceSession = async (user: TUsers, ip: string, userAgent: string) => {
|
||||
const devices = await UserDeviceSchema.parseAsync(user.devices || []);
|
||||
const isDeviceSeen = devices.some(
|
||||
(device) => device.ip === ip && device.userAgent === userAgent
|
||||
);
|
||||
const isDeviceSeen = devices.some((device) => device.ip === ip && device.userAgent === userAgent);
|
||||
|
||||
if (!isDeviceSeen) {
|
||||
const newDeviceList = devices.concat([{ ip, userAgent }]);
|
||||
@@ -159,8 +153,7 @@ export const authLoginServiceFactory = ({
|
||||
validateProviderAuthToken(providerAuthToken as string, email);
|
||||
}
|
||||
|
||||
if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey)
|
||||
throw new Error("Failed to authenticate. Try again?");
|
||||
if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) throw new Error("Failed to authenticate. Try again?");
|
||||
const isValidClientProof = await srpCheckClientProof(
|
||||
userEnc.salt,
|
||||
userEnc.verifier,
|
||||
@@ -176,11 +169,9 @@ export const authLoginServiceFactory = ({
|
||||
});
|
||||
// send multi factor auth token if they it enabled
|
||||
if (userEnc.isMfaEnabled) {
|
||||
const mfaToken = jwt.sign(
|
||||
{ authTokenType: AuthTokenType.MFA_TOKEN, userId: userEnc.userId },
|
||||
cfg.AUTH_SECRET,
|
||||
{ expiresIn: cfg.JWT_MFA_LIFETIME }
|
||||
);
|
||||
const mfaToken = jwt.sign({ authTokenType: AuthTokenType.MFA_TOKEN, userId: userEnc.userId }, cfg.AUTH_SECRET, {
|
||||
expiresIn: cfg.JWT_MFA_LIFETIME
|
||||
});
|
||||
await sendUserMfaCode(userEnc.userId, userEnc.email);
|
||||
|
||||
return { isMfaEnabled: true, token: mfaToken } as const;
|
||||
@@ -230,8 +221,7 @@ export const authLoginServiceFactory = ({
|
||||
let user = await userDAL.findUserByEmail(email);
|
||||
const appCfg = getConfig();
|
||||
const isOauthSignUpDisabled = !isSignupAllowed && !user;
|
||||
if (isOauthSignUpDisabled)
|
||||
throw new BadRequestError({ message: "User signup disabled", name: "Oauth 2 login" });
|
||||
if (isOauthSignUpDisabled) throw new BadRequestError({ message: "User signup disabled", name: "Oauth 2 login" });
|
||||
|
||||
if (!user) {
|
||||
user = await userDAL.create({ email, firstName, lastName, authMethods: [authMethod] });
|
||||
|
||||
@@ -9,11 +9,7 @@ import { TokenType } from "../auth-token/auth-token-types";
|
||||
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||
import { TUserDALFactory } from "../user/user-dal";
|
||||
import { TAuthDALFactory } from "./auth-dal";
|
||||
import {
|
||||
TChangePasswordDTO,
|
||||
TCreateBackupPrivateKeyDTO,
|
||||
TResetPasswordViaBackupKeyDTO
|
||||
} from "./auth-password-type";
|
||||
import { TChangePasswordDTO, TCreateBackupPrivateKeyDTO, TResetPasswordViaBackupKeyDTO } from "./auth-password-type";
|
||||
import { AuthTokenType } from "./auth-type";
|
||||
|
||||
type TAuthPasswordServiceFactoryDep = {
|
||||
@@ -70,8 +66,7 @@ export const authPaswordServiceFactory = ({
|
||||
serverPrivateKey: null,
|
||||
clientPublicKey: null
|
||||
});
|
||||
if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey)
|
||||
throw new Error("Failed to authenticate. Try again?");
|
||||
if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) throw new Error("Failed to authenticate. Try again?");
|
||||
const isValidClientProof = await srpCheckClientProof(
|
||||
userEnc.salt,
|
||||
userEnc.verifier,
|
||||
@@ -200,8 +195,7 @@ export const authPaswordServiceFactory = ({
|
||||
throw new Error("Failed to find user");
|
||||
}
|
||||
|
||||
if (!userEnc.clientPublicKey || !userEnc.serverPrivateKey)
|
||||
throw new Error("failed to create backup key");
|
||||
if (!userEnc.clientPublicKey || !userEnc.serverPrivateKey) throw new Error("failed to create backup key");
|
||||
const isValidClientProff = await srpCheckClientProof(
|
||||
userEnc.salt,
|
||||
userEnc.verifier,
|
||||
|
||||
@@ -148,9 +148,7 @@ export const authSignupServiceFactory = ({
|
||||
});
|
||||
|
||||
const hasSamlEnabled = user?.authMethods?.some((authMethod) =>
|
||||
[AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes(
|
||||
authMethod as AuthMethod
|
||||
)
|
||||
[AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes(authMethod as AuthMethod)
|
||||
);
|
||||
|
||||
if (!hasSamlEnabled) {
|
||||
@@ -162,9 +160,7 @@ export const authSignupServiceFactory = ({
|
||||
{ userId: user.id, status: OrgMembershipStatus.Accepted }
|
||||
);
|
||||
const uniqueOrgId = [...new Set(updatedMembersips.map(({ orgId }) => orgId))];
|
||||
await Promise.allSettled(
|
||||
uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId))
|
||||
);
|
||||
await Promise.allSettled(uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId)));
|
||||
|
||||
const tokenSession = await tokenService.getUserTokenSession({
|
||||
userAgent,
|
||||
@@ -259,9 +255,7 @@ export const authSignupServiceFactory = ({
|
||||
tx
|
||||
);
|
||||
const uniqueOrgId = [...new Set(updatedMembersips.map(({ orgId }) => orgId))];
|
||||
await Promise.allSettled(
|
||||
uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId))
|
||||
);
|
||||
await Promise.allSettled(uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId)));
|
||||
|
||||
return { info: us, key: userEncKey };
|
||||
});
|
||||
|
||||
@@ -14,11 +14,7 @@ export const identityAccessTokenDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const doc = await (tx || db)(TableName.IdentityAccessToken)
|
||||
.where(filter)
|
||||
.join(
|
||||
TableName.Identity,
|
||||
`${TableName.Identity}.id`,
|
||||
`${TableName.IdentityAccessToken}.identityId`
|
||||
)
|
||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.IdentityAccessToken}.identityId`)
|
||||
.leftJoin(
|
||||
TableName.IdentityUaClientSecret,
|
||||
`${TableName.IdentityAccessToken}.identityUAClientSecretId`,
|
||||
|
||||
@@ -7,18 +7,13 @@ import { checkIPAgainstBlocklist, TIp } from "@app/lib/ip";
|
||||
|
||||
import { AuthTokenType } from "../auth/auth-type";
|
||||
import { TIdentityAccessTokenDALFactory } from "./identity-access-token-dal";
|
||||
import {
|
||||
TIdentityAccessTokenJwtPayload,
|
||||
TRenewAccessTokenDTO
|
||||
} from "./identity-access-token-types";
|
||||
import { TIdentityAccessTokenJwtPayload, TRenewAccessTokenDTO } from "./identity-access-token-types";
|
||||
|
||||
type TIdentityAccessTokenServiceFactoryDep = {
|
||||
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
|
||||
};
|
||||
|
||||
export type TIdentityAccessTokenServiceFactory = ReturnType<
|
||||
typeof identityAccessTokenServiceFactory
|
||||
>;
|
||||
export type TIdentityAccessTokenServiceFactory = ReturnType<typeof identityAccessTokenServiceFactory>;
|
||||
|
||||
export const identityAccessTokenServiceFactory = ({
|
||||
identityAccessTokenDAL
|
||||
@@ -33,11 +28,7 @@ export const identityAccessTokenServiceFactory = ({
|
||||
createdAt: accessTokenCreatedAt
|
||||
} = identityAccessToken;
|
||||
|
||||
if (
|
||||
accessTokenNumUsesLimit > 0 &&
|
||||
accessTokenNumUses > 0 &&
|
||||
accessTokenNumUses >= accessTokenNumUsesLimit
|
||||
) {
|
||||
if (accessTokenNumUsesLimit > 0 && accessTokenNumUses > 0 && accessTokenNumUses >= accessTokenNumUsesLimit) {
|
||||
throw new BadRequestError({
|
||||
message: "Unable to renew because access token number of uses limit reached"
|
||||
});
|
||||
@@ -95,8 +86,7 @@ export const identityAccessTokenServiceFactory = ({
|
||||
const decodedToken = jwt.verify(accessToken, appCfg.AUTH_SECRET) as JwtPayload & {
|
||||
identityAccessTokenId: string;
|
||||
};
|
||||
if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN)
|
||||
throw new UnauthorizedError();
|
||||
if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) throw new UnauthorizedError();
|
||||
|
||||
const identityAccessToken = await identityAccessTokenDAL.findOne({
|
||||
[`${TableName.IdentityAccessToken}.id` as "id"]: decodedToken.identityAccessTokenId,
|
||||
@@ -106,20 +96,14 @@ export const identityAccessTokenServiceFactory = ({
|
||||
|
||||
validateAccessTokenExp(identityAccessToken);
|
||||
|
||||
const updatedIdentityAccessToken = await identityAccessTokenDAL.updateById(
|
||||
identityAccessToken.id,
|
||||
{
|
||||
accessTokenLastRenewedAt: new Date()
|
||||
}
|
||||
);
|
||||
const updatedIdentityAccessToken = await identityAccessTokenDAL.updateById(identityAccessToken.id, {
|
||||
accessTokenLastRenewedAt: new Date()
|
||||
});
|
||||
|
||||
return { accessToken, identityAccessToken: updatedIdentityAccessToken };
|
||||
};
|
||||
|
||||
const fnValidateIdentityAccessToken = async (
|
||||
token: TIdentityAccessTokenJwtPayload,
|
||||
ipAddress?: string
|
||||
) => {
|
||||
const fnValidateIdentityAccessToken = async (token: TIdentityAccessTokenJwtPayload, ipAddress?: string) => {
|
||||
const identityAccessToken = await identityAccessTokenDAL.findOne({
|
||||
[`${TableName.IdentityAccessToken}.id` as "id"]: token.identityAccessTokenId,
|
||||
isAccessTokenRevoked: false
|
||||
|
||||
@@ -14,11 +14,7 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const docs = await (tx || db)(TableName.IdentityProjectMembership)
|
||||
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
|
||||
.join(
|
||||
TableName.Identity,
|
||||
`${TableName.IdentityProjectMembership}.identityId`,
|
||||
`${TableName.Identity}.id`
|
||||
)
|
||||
.join(TableName.Identity, `${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`)
|
||||
.leftJoin(
|
||||
TableName.ProjectRoles,
|
||||
`${TableName.IdentityProjectMembership}.roleId`,
|
||||
|
||||
@@ -2,10 +2,7 @@ import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { ProjectMembershipRole, TProjectRoles } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
|
||||
|
||||
@@ -24,10 +21,7 @@ type TIdentityProjectServiceFactoryDep = {
|
||||
identityProjectDAL: TIdentityProjectDALFactory;
|
||||
projectDAL: Pick<TProjectDALFactory, "findById">;
|
||||
identityOrgMembershipDAL: Pick<TIdentityOrgDALFactory, "findOne">;
|
||||
permissionService: Pick<
|
||||
TPermissionServiceFactory,
|
||||
"getProjectPermission" | "getProjectPermissionByRole"
|
||||
>;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getProjectPermissionByRole">;
|
||||
};
|
||||
|
||||
export type TIdentityProjectServiceFactory = ReturnType<typeof identityProjectServiceFactory>;
|
||||
@@ -38,18 +32,9 @@ export const identityProjectServiceFactory = ({
|
||||
identityOrgMembershipDAL,
|
||||
projectDAL
|
||||
}: TIdentityProjectServiceFactoryDep) => {
|
||||
const createProjectIdentity = async ({
|
||||
identityId,
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
role
|
||||
}: TCreateProjectIdentityDTO) => {
|
||||
const createProjectIdentity = async ({ identityId, actor, actorId, projectId, role }: TCreateProjectIdentityDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Identity
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Identity);
|
||||
|
||||
const existingIdentity = await identityProjectDAL.findOne({ identityId, projectId });
|
||||
if (existingIdentity)
|
||||
@@ -67,8 +52,10 @@ export const identityProjectServiceFactory = ({
|
||||
message: `Failed to find identity with id ${identityId}`
|
||||
});
|
||||
|
||||
const { permission: rolePermission, role: customRole } =
|
||||
await permissionService.getProjectPermissionByRole(role, project.id);
|
||||
const { permission: rolePermission, role: customRole } = await permissionService.getProjectPermissionByRole(
|
||||
role,
|
||||
project.id
|
||||
);
|
||||
const hasPriviledge = isAtLeastAsPrivileged(permission, rolePermission);
|
||||
if (!hasPriviledge)
|
||||
throw new ForbiddenRequestError({
|
||||
@@ -85,18 +72,9 @@ export const identityProjectServiceFactory = ({
|
||||
return projectIdentity;
|
||||
};
|
||||
|
||||
const updateProjectIdentity = async ({
|
||||
projectId,
|
||||
identityId,
|
||||
role,
|
||||
actor,
|
||||
actorId
|
||||
}: TUpdateProjectIdentityDTO) => {
|
||||
const updateProjectIdentity = async ({ projectId, identityId, role, actor, actorId }: TUpdateProjectIdentityDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.Identity
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
|
||||
|
||||
const projectIdentity = await identityProjectDAL.findOne({ identityId, projectId });
|
||||
if (!projectIdentity)
|
||||
@@ -115,8 +93,10 @@ export const identityProjectServiceFactory = ({
|
||||
|
||||
let customRole: TProjectRoles | undefined;
|
||||
if (role) {
|
||||
const { permission: rolePermission, role: customOrgRole } =
|
||||
await permissionService.getProjectPermissionByRole(role, projectIdentity.projectId);
|
||||
const { permission: rolePermission, role: customOrgRole } = await permissionService.getProjectPermissionByRole(
|
||||
role,
|
||||
projectIdentity.projectId
|
||||
);
|
||||
|
||||
const isCustomRole = Boolean(customOrgRole);
|
||||
const hasRequiredNewRolePermission = isAtLeastAsPrivileged(permission, rolePermission);
|
||||
@@ -135,12 +115,7 @@ export const identityProjectServiceFactory = ({
|
||||
return updatedProjectIdentity;
|
||||
};
|
||||
|
||||
const deleteProjectIdentity = async ({
|
||||
identityId,
|
||||
actorId,
|
||||
actor,
|
||||
projectId
|
||||
}: TDeleteProjectIdentityDTO) => {
|
||||
const deleteProjectIdentity = async ({ identityId, actorId, actor, projectId }: TDeleteProjectIdentityDTO) => {
|
||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
||||
if (!identityProjectMembership)
|
||||
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
|
||||
@@ -150,10 +125,7 @@ export const identityProjectServiceFactory = ({
|
||||
actorId,
|
||||
identityProjectMembership.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.Identity
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Identity);
|
||||
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
|
||||
ActorType.IDENTITY,
|
||||
identityId,
|
||||
@@ -169,10 +141,7 @@ export const identityProjectServiceFactory = ({
|
||||
|
||||
const listProjectIdentities = async ({ projectId, actor, actorId }: TListProjectIdentityDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Identity
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Identity);
|
||||
|
||||
const identityMemberhips = await identityProjectDAL.findByProjectId(projectId);
|
||||
return identityMemberhips;
|
||||
|
||||
@@ -6,10 +6,7 @@ import jwt from "jsonwebtoken";
|
||||
|
||||
import { IdentityAuthMethod } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects
|
||||
} from "@app/ee/services/permission/org-permission";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
@@ -71,8 +68,7 @@ export const identityUaServiceFactory = ({
|
||||
);
|
||||
if (!validClientSecretInfo) throw new UnauthorizedError();
|
||||
|
||||
const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } =
|
||||
validClientSecretInfo;
|
||||
const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } = validClientSecretInfo;
|
||||
if (clientSecretTTL > 0) {
|
||||
const clientSecretCreated = new Date(validClientSecretInfo.createdAt);
|
||||
const ttlInMilliseconds = clientSecretTTL * 1000;
|
||||
@@ -97,16 +93,12 @@ export const identityUaServiceFactory = ({
|
||||
isClientSecretRevoked: true
|
||||
});
|
||||
throw new UnauthorizedError({
|
||||
message:
|
||||
"Failed to authenticate identity credentials due to client secret number of uses limit reached"
|
||||
message: "Failed to authenticate identity credentials due to client secret number of uses limit reached"
|
||||
});
|
||||
}
|
||||
|
||||
const identityAccessToken = await identityUaDAL.transaction(async (tx) => {
|
||||
const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(
|
||||
validClientSecretInfo.id,
|
||||
tx
|
||||
);
|
||||
const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo.id, tx);
|
||||
const newToken = await identityAccessTokenDAL.create(
|
||||
{
|
||||
identityId: identityUa.identityId,
|
||||
@@ -132,10 +124,7 @@ export const identityUaServiceFactory = ({
|
||||
} as TIdentityAccessTokenJwtPayload,
|
||||
appCfg.AUTH_SECRET,
|
||||
{
|
||||
expiresIn:
|
||||
identityAccessToken.accessTokenMaxTTL === 0
|
||||
? undefined
|
||||
: identityAccessToken.accessTokenMaxTTL
|
||||
expiresIn: identityAccessToken.accessTokenMaxTTL === 0 ? undefined : identityAccessToken.accessTokenMaxTTL
|
||||
}
|
||||
);
|
||||
return { accessToken, identityUa, validClientSecretInfo, identityAccessToken };
|
||||
@@ -162,35 +151,26 @@ export const identityUaServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.orgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.orgId);
|
||||
const reformattedClientSecretTrustedIps = clientSecretTrustedIps.map(
|
||||
(clientSecretTrustedIp) => {
|
||||
if (
|
||||
!plan.ipAllowlisting &&
|
||||
clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" &&
|
||||
clientSecretTrustedIp.ipAddress !== "::/0"
|
||||
)
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range."
|
||||
});
|
||||
if (!isValidIpOrCidr(clientSecretTrustedIp.ipAddress))
|
||||
throw new BadRequestError({
|
||||
message: "The IP is not a valid IPv4, IPv6, or CIDR block"
|
||||
});
|
||||
return extractIPDetails(clientSecretTrustedIp.ipAddress);
|
||||
}
|
||||
);
|
||||
const reformattedClientSecretTrustedIps = clientSecretTrustedIps.map((clientSecretTrustedIp) => {
|
||||
if (
|
||||
!plan.ipAllowlisting &&
|
||||
clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" &&
|
||||
clientSecretTrustedIp.ipAddress !== "::/0"
|
||||
)
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range."
|
||||
});
|
||||
if (!isValidIpOrCidr(clientSecretTrustedIp.ipAddress))
|
||||
throw new BadRequestError({
|
||||
message: "The IP is not a valid IPv4, IPv6, or CIDR block"
|
||||
});
|
||||
return extractIPDetails(clientSecretTrustedIp.ipAddress);
|
||||
});
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => {
|
||||
if (
|
||||
!plan.ipAllowlisting &&
|
||||
@@ -254,41 +234,31 @@ export const identityUaServiceFactory = ({
|
||||
|
||||
if (
|
||||
(accessTokenMaxTTL || uaIdentityAuth.accessTokenMaxTTL) > 0 &&
|
||||
(accessTokenTTL || uaIdentityAuth.accessTokenMaxTTL) >
|
||||
(accessTokenMaxTTL || uaIdentityAuth.accessTokenMaxTTL)
|
||||
(accessTokenTTL || uaIdentityAuth.accessTokenMaxTTL) > (accessTokenMaxTTL || uaIdentityAuth.accessTokenMaxTTL)
|
||||
) {
|
||||
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.orgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const plan = await licenseService.getPlan(identityMembershipOrg.orgId);
|
||||
const reformattedClientSecretTrustedIps = clientSecretTrustedIps?.map(
|
||||
(clientSecretTrustedIp) => {
|
||||
if (
|
||||
!plan.ipAllowlisting &&
|
||||
clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" &&
|
||||
clientSecretTrustedIp.ipAddress !== "::/0"
|
||||
)
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range."
|
||||
});
|
||||
if (!isValidIpOrCidr(clientSecretTrustedIp.ipAddress))
|
||||
throw new BadRequestError({
|
||||
message: "The IP is not a valid IPv4, IPv6, or CIDR block"
|
||||
});
|
||||
return extractIPDetails(clientSecretTrustedIp.ipAddress);
|
||||
}
|
||||
);
|
||||
const reformattedClientSecretTrustedIps = clientSecretTrustedIps?.map((clientSecretTrustedIp) => {
|
||||
if (
|
||||
!plan.ipAllowlisting &&
|
||||
clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" &&
|
||||
clientSecretTrustedIp.ipAddress !== "::/0"
|
||||
)
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range."
|
||||
});
|
||||
if (!isValidIpOrCidr(clientSecretTrustedIp.ipAddress))
|
||||
throw new BadRequestError({
|
||||
message: "The IP is not a valid IPv4, IPv6, or CIDR block"
|
||||
});
|
||||
return extractIPDetails(clientSecretTrustedIp.ipAddress);
|
||||
});
|
||||
const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => {
|
||||
if (
|
||||
!plan.ipAllowlisting &&
|
||||
@@ -330,15 +300,8 @@ export const identityUaServiceFactory = ({
|
||||
|
||||
const uaIdentityAuth = await identityUaDAL.findOne({ identityId });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.orgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
|
||||
return { ...uaIdentityAuth, orgId: identityMembershipOrg.orgId };
|
||||
};
|
||||
|
||||
@@ -356,15 +319,8 @@ export const identityUaServiceFactory = ({
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have universal auth"
|
||||
});
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.orgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
@@ -409,15 +365,8 @@ export const identityUaServiceFactory = ({
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have universal auth"
|
||||
});
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.orgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
@@ -441,27 +390,15 @@ export const identityUaServiceFactory = ({
|
||||
return { clientSecrets, orgId: identityMembershipOrg.orgId };
|
||||
};
|
||||
|
||||
const revokeUaClientSecret = async ({
|
||||
identityId,
|
||||
actorId,
|
||||
actor,
|
||||
clientSecretId
|
||||
}: TRevokeUaClientSecretDTO) => {
|
||||
const revokeUaClientSecret = async ({ identityId, actorId, actor, clientSecretId }: TRevokeUaClientSecretDTO) => {
|
||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
|
||||
if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" });
|
||||
if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral)
|
||||
throw new BadRequestError({
|
||||
message: "The identity does not have universal auth"
|
||||
});
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
actorId,
|
||||
identityMembershipOrg.orgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Delete,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
|
||||
@@ -14,11 +14,7 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const [data] = await (tx || db)(TableName.IdentityOrgMembership)
|
||||
.where(filter)
|
||||
.join(
|
||||
TableName.Identity,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Identity}.id`
|
||||
)
|
||||
.join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`)
|
||||
.select(selectAllTableCols(TableName.IdentityOrgMembership))
|
||||
.select(db.ref("name").withSchema(TableName.Identity))
|
||||
.select(db.ref("authMethod").withSchema(TableName.Identity));
|
||||
@@ -35,16 +31,8 @@ export const identityOrgDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const docs = await (tx || db)(TableName.IdentityOrgMembership)
|
||||
.where(`${TableName.IdentityOrgMembership}.orgId`, orgId)
|
||||
.join(
|
||||
TableName.Identity,
|
||||
`${TableName.IdentityOrgMembership}.identityId`,
|
||||
`${TableName.Identity}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.OrgRoles,
|
||||
`${TableName.IdentityOrgMembership}.roleId`,
|
||||
`${TableName.OrgRoles}.id`
|
||||
)
|
||||
.join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`)
|
||||
.leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
|
||||
.select(selectAllTableCols(TableName.IdentityOrgMembership))
|
||||
// cr stands for custom role
|
||||
.select(db.ref("id").as("crId").withSchema(TableName.OrgRoles))
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { OrgMembershipRole, TOrgRoles } from "@app/db/schemas";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects
|
||||
} from "@app/ee/services/permission/org-permission";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
|
||||
@@ -30,17 +27,15 @@ export const identityServiceFactory = ({
|
||||
}: TIdentityServiceFactoryDep) => {
|
||||
const createIdentity = async ({ name, role, actor, orgId, actorId }: TCreateIdentityDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: rolePermission, role: customRole } =
|
||||
await permissionService.getOrgPermissionByRole(role, orgId);
|
||||
const { permission: rolePermission, role: customRole } = await permissionService.getOrgPermissionByRole(
|
||||
role,
|
||||
orgId
|
||||
);
|
||||
const isCustomRole = Boolean(customRole);
|
||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, rolePermission);
|
||||
if (!hasRequiredPriviledges)
|
||||
throw new BadRequestError({ message: "Failed to create a more privileged identity" });
|
||||
if (!hasRequiredPriviledges) throw new BadRequestError({ message: "Failed to create a more privileged identity" });
|
||||
|
||||
const identity = await identityDAL.transaction(async (tx) => {
|
||||
const newIdentity = await identityDAL.create({ name }, tx);
|
||||
@@ -61,18 +56,10 @@ export const identityServiceFactory = ({
|
||||
|
||||
const updateIdentity = async ({ id, role, name, actor, actorId }: TUpdateIdentityDTO) => {
|
||||
const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: id });
|
||||
if (!identityOrgMembership)
|
||||
throw new BadRequestError({ message: `Failed to find identity with id ${id}` });
|
||||
if (!identityOrgMembership) throw new BadRequestError({ message: `Failed to find identity with id ${id}` });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
actorId,
|
||||
identityOrgMembership.orgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityOrgMembership.orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
||||
|
||||
const { permission: identityRolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
@@ -85,8 +72,10 @@ export const identityServiceFactory = ({
|
||||
|
||||
let customRole: TOrgRoles | undefined;
|
||||
if (role) {
|
||||
const { permission: rolePermission, role: customOrgRole } =
|
||||
await permissionService.getOrgPermissionByRole(role, identityOrgMembership.orgId);
|
||||
const { permission: rolePermission, role: customOrgRole } = await permissionService.getOrgPermissionByRole(
|
||||
role,
|
||||
identityOrgMembership.orgId
|
||||
);
|
||||
|
||||
const isCustomRole = Boolean(customOrgRole);
|
||||
const hasRequiredNewRolePermission = isAtLeastAsPrivileged(permission, rolePermission);
|
||||
@@ -96,9 +85,7 @@ export const identityServiceFactory = ({
|
||||
}
|
||||
|
||||
const identity = await identityDAL.transaction(async (tx) => {
|
||||
const newIdentity = name
|
||||
? await identityDAL.updateById(id, { name }, tx)
|
||||
: await identityDAL.findById(id, tx);
|
||||
const newIdentity = name ? await identityDAL.updateById(id, { name }, tx) : await identityDAL.findById(id, tx);
|
||||
if (role) {
|
||||
await identityOrgMembershipDAL.update(
|
||||
{ identityId: id },
|
||||
@@ -117,18 +104,10 @@ export const identityServiceFactory = ({
|
||||
|
||||
const deleteIdentity = async ({ actorId, actor, id }: TDeleteIdentityDTO) => {
|
||||
const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: id });
|
||||
if (!identityOrgMembership)
|
||||
throw new BadRequestError({ message: `Failed to find identity with id ${id}` });
|
||||
if (!identityOrgMembership) throw new BadRequestError({ message: `Failed to find identity with id ${id}` });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor,
|
||||
actorId,
|
||||
identityOrgMembership.orgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Delete,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, identityOrgMembership.orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity);
|
||||
const { permission: identityRolePermission } = await permissionService.getOrgPermission(
|
||||
ActorType.IDENTITY,
|
||||
id,
|
||||
@@ -144,10 +123,7 @@ export const identityServiceFactory = ({
|
||||
|
||||
const listOrgIdentities = async ({ orgId, actor, actorId }: TOrgPermission) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Identity
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
|
||||
|
||||
const identityMemberhips = await identityOrgMembershipDAL.findByOrgId(orgId);
|
||||
return identityMemberhips;
|
||||
|
||||
@@ -22,11 +22,7 @@ const getAppsGCPSecretManager = async ({ accessToken }: { accessToken: string })
|
||||
interface GCPApp {
|
||||
projectNumber: string;
|
||||
projectId: string;
|
||||
lifecycleState:
|
||||
| "ACTIVE"
|
||||
| "LIFECYCLE_STATE_UNSPECIFIED"
|
||||
| "DELETE_REQUESTED"
|
||||
| "DELETE_IN_PROGRESS";
|
||||
lifecycleState: "ACTIVE" | "LIFECYCLE_STATE_UNSPECIFIED" | "DELETE_REQUESTED" | "DELETE_IN_PROGRESS";
|
||||
name: string;
|
||||
createTime: string;
|
||||
parent: {
|
||||
@@ -131,30 +127,21 @@ const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => {
|
||||
/**
|
||||
* Return list of names of apps for Vercel integration
|
||||
*/
|
||||
const getAppsVercel = async ({
|
||||
accessToken,
|
||||
teamId
|
||||
}: {
|
||||
teamId?: string | null;
|
||||
accessToken: string;
|
||||
}) => {
|
||||
const getAppsVercel = async ({ accessToken, teamId }: { teamId?: string | null; accessToken: string }) => {
|
||||
const res = (
|
||||
await request.get<{ projects: { name: string; id: string }[] }>(
|
||||
`${IntegrationUrls.VERCEL_API_URL}/v9/projects`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
},
|
||||
...(teamId
|
||||
? {
|
||||
params: {
|
||||
teamId
|
||||
}
|
||||
await request.get<{ projects: { name: string; id: string }[] }>(`${IntegrationUrls.VERCEL_API_URL}/v9/projects`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
},
|
||||
...(teamId
|
||||
? {
|
||||
params: {
|
||||
teamId
|
||||
}
|
||||
: {})
|
||||
}
|
||||
)
|
||||
}
|
||||
: {})
|
||||
})
|
||||
).data;
|
||||
|
||||
const apps = res.projects.map((a) => ({
|
||||
@@ -273,16 +260,13 @@ const getAppsGithub = async ({ accessToken }: { accessToken: string }) => {
|
||||
*/
|
||||
const getAppsRender = async ({ accessToken }: { accessToken: string }) => {
|
||||
const res = (
|
||||
await request.get<{ service: { name: string; id: string } }[]>(
|
||||
`${IntegrationUrls.RENDER_API_URL}/v1/services`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json",
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
await request.get<{ service: { name: string; id: string } }[]>(`${IntegrationUrls.RENDER_API_URL}/v1/services`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json",
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
)
|
||||
})
|
||||
).data;
|
||||
|
||||
const apps = res.map((a) => ({
|
||||
@@ -346,13 +330,7 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => {
|
||||
/**
|
||||
* Return list of sites for Laravel Forge integration
|
||||
*/
|
||||
const getAppsLaravelForge = async ({
|
||||
accessToken,
|
||||
serverId
|
||||
}: {
|
||||
accessToken: string;
|
||||
serverId?: string;
|
||||
}) => {
|
||||
const getAppsLaravelForge = async ({ accessToken, serverId }: { accessToken: string; serverId?: string }) => {
|
||||
const res = (
|
||||
await request.get<{ sites: { name: string; id: string }[] }>(
|
||||
`${IntegrationUrls.LARAVELFORGE_API_URL}/api/v1/servers/${serverId}/sites`,
|
||||
@@ -464,13 +442,7 @@ const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => {
|
||||
/**
|
||||
* Return list of projects for Terraform Cloud integration
|
||||
*/
|
||||
const getAppsTerraformCloud = async ({
|
||||
accessToken,
|
||||
workspacesId
|
||||
}: {
|
||||
accessToken: string;
|
||||
workspacesId?: string;
|
||||
}) => {
|
||||
const getAppsTerraformCloud = async ({ accessToken, workspacesId }: { accessToken: string; workspacesId?: string }) => {
|
||||
const res = (
|
||||
await request.get<{ data: { attributes: { name: string }; id: string } }>(
|
||||
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${workspacesId}`,
|
||||
@@ -566,16 +538,13 @@ const getAppsGitlab = async ({
|
||||
per_page: String(perPage)
|
||||
});
|
||||
|
||||
const { data } = await request.get<{ name: string; id: string }[]>(
|
||||
`${gitLabApiUrl}/v4/users/${id}/projects`,
|
||||
{
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
const { data } = await request.get<{ name: string; id: string }[]>(`${gitLabApiUrl}/v4/users/${id}/projects`, {
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
data.forEach((a) => {
|
||||
apps.push({
|
||||
@@ -642,15 +611,12 @@ const getAppsSupabase = async ({ accessToken }: { accessToken: string }) => {
|
||||
* Return list of accounts for the Checkly integration
|
||||
*/
|
||||
const getAppsCheckly = async ({ accessToken }: { accessToken: string }) => {
|
||||
const { data } = await request.get<{ name: string; id: string }[]>(
|
||||
`${IntegrationUrls.CHECKLY_API_URL}/v1/accounts`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
const { data } = await request.get<{ name: string; id: string }[]>(`${IntegrationUrls.CHECKLY_API_URL}/v1/accounts`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const apps = data.map((a) => ({
|
||||
name: a.name,
|
||||
@@ -663,13 +629,7 @@ const getAppsCheckly = async ({ accessToken }: { accessToken: string }) => {
|
||||
/**
|
||||
* Return list of projects for the Cloudflare Pages integration
|
||||
*/
|
||||
const getAppsCloudflarePages = async ({
|
||||
accessToken,
|
||||
accountId
|
||||
}: {
|
||||
accessToken: string;
|
||||
accountId?: string;
|
||||
}) => {
|
||||
const getAppsCloudflarePages = async ({ accessToken, accountId }: { accessToken: string; accountId?: string }) => {
|
||||
const { data } = await request.get<{ result: { name: string; id: string }[] }>(
|
||||
`${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accountId}/pages/projects`,
|
||||
{
|
||||
@@ -690,13 +650,7 @@ const getAppsCloudflarePages = async ({
|
||||
/**
|
||||
* Return list of projects for the Cloudflare Workers integration
|
||||
*/
|
||||
const getAppsCloudflareWorkers = async ({
|
||||
accessToken,
|
||||
accountId
|
||||
}: {
|
||||
accessToken: string;
|
||||
accountId?: string;
|
||||
}) => {
|
||||
const getAppsCloudflareWorkers = async ({ accessToken, accountId }: { accessToken: string; accountId?: string }) => {
|
||||
const { data } = await request.get<{ result: { id: string }[] }>(
|
||||
`${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accountId}/workers/services`,
|
||||
{
|
||||
@@ -717,13 +671,7 @@ const getAppsCloudflareWorkers = async ({
|
||||
/**
|
||||
* Return list of repositories for the BitBucket integration based on provided BitBucket workspace
|
||||
*/
|
||||
const getAppsBitBucket = async ({
|
||||
accessToken,
|
||||
workspaceSlug
|
||||
}: {
|
||||
accessToken: string;
|
||||
workspaceSlug?: string;
|
||||
}) => {
|
||||
const getAppsBitBucket = async ({ accessToken, workspaceSlug }: { accessToken: string; workspaceSlug?: string }) => {
|
||||
interface RepositoriesResponse {
|
||||
size: number;
|
||||
page: number;
|
||||
@@ -881,25 +829,19 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => {
|
||||
|
||||
// is write access is allowed then delete the created secrets from workspace
|
||||
if (writeUser && writeFolder) {
|
||||
await request.delete(
|
||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${userPath}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
await request.delete(`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${userPath}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
await request.delete(
|
||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${folderPath}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
await request.delete(`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${folderPath}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
@@ -910,9 +852,7 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => {
|
||||
});
|
||||
|
||||
const appsWriteResponses = await Promise.all(writeAccessCheck);
|
||||
const appsWithWriteAccess = appsWriteResponses.filter(
|
||||
(appRes) => !(appRes as { error: string })?.error
|
||||
);
|
||||
const appsWithWriteAccess = appsWriteResponses.filter((appRes) => !(appRes as { error: string })?.error);
|
||||
|
||||
const apps = (appsWithWriteAccess as { id: string; name: string }[]).map((a) => ({
|
||||
name: a.name,
|
||||
@@ -945,15 +885,12 @@ const getAppsDigitalOceanAppPlatform = async ({ accessToken }: { accessToken: st
|
||||
}
|
||||
|
||||
const res = (
|
||||
await request.get<{ apps: DigitalOceanApp[] }>(
|
||||
`${IntegrationUrls.DIGITAL_OCEAN_API_URL}/v2/apps`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
await request.get<{ apps: DigitalOceanApp[] }>(`${IntegrationUrls.DIGITAL_OCEAN_API_URL}/v2/apps`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
)
|
||||
})
|
||||
).data;
|
||||
|
||||
return (res.apps ?? []).map((a) => ({
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import {
|
||||
SecretEncryptionAlgo,
|
||||
SecretKeyEncoding,
|
||||
TIntegrationAuths,
|
||||
TIntegrationAuthsInsert
|
||||
} from "@app/db/schemas";
|
||||
import { SecretEncryptionAlgo, SecretKeyEncoding, TIntegrationAuths, TIntegrationAuthsInsert } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import {
|
||||
decryptSymmetric128BitHexKeyUTF8,
|
||||
encryptSymmetric128BitHexKeyUTF8
|
||||
} from "@app/lib/crypto";
|
||||
import { decryptSymmetric128BitHexKeyUTF8, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { TProjectPermission } from "@app/lib/types";
|
||||
|
||||
@@ -70,16 +59,9 @@ export const integrationAuthServiceFactory = ({
|
||||
projectBotDAL,
|
||||
projectBotService
|
||||
}: TIntegrationAuthServiceFactoryDep) => {
|
||||
const listIntegrationAuthByProjectId = async ({
|
||||
actorId,
|
||||
actor,
|
||||
projectId
|
||||
}: TProjectPermission) => {
|
||||
const listIntegrationAuthByProjectId = async ({ actorId, actor, projectId }: TProjectPermission) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const authorizations = await integrationAuthDAL.find({ projectId });
|
||||
return authorizations;
|
||||
};
|
||||
@@ -88,38 +70,20 @@ export const integrationAuthServiceFactory = ({
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
return integrationAuth;
|
||||
};
|
||||
|
||||
const oauthExchange = async ({
|
||||
projectId,
|
||||
actorId,
|
||||
actor,
|
||||
integration,
|
||||
url,
|
||||
code
|
||||
}: TOauthExchangeDTO) => {
|
||||
const oauthExchange = async ({ projectId, actorId, actor, integration, url, code }: TOauthExchangeDTO) => {
|
||||
if (!Object.values(Integrations).includes(integration as Integrations))
|
||||
throw new BadRequestError({ message: "Invalid integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
|
||||
|
||||
const bot = await projectBotDAL.findOne({ isActive: true, projectId });
|
||||
if (!bot)
|
||||
throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" });
|
||||
if (!bot) throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" });
|
||||
|
||||
const tokenExchange = await exchangeCode({ integration, code, url });
|
||||
const updateDoc: TIntegrationAuthsInsert = {
|
||||
@@ -178,14 +142,10 @@ export const integrationAuthServiceFactory = ({
|
||||
throw new BadRequestError({ message: "Invalid integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
|
||||
|
||||
const bot = await projectBotDAL.findOne({ isActive: true, projectId });
|
||||
if (!bot)
|
||||
throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" });
|
||||
if (!bot) throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" });
|
||||
|
||||
const updateDoc: TIntegrationAuthsInsert = {
|
||||
projectId,
|
||||
@@ -251,11 +211,7 @@ export const integrationAuthServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
integrationAuth.refreshCiphertext &&
|
||||
integrationAuth.refreshIV &&
|
||||
integrationAuth.refreshTag
|
||||
) {
|
||||
if (integrationAuth.refreshCiphertext && integrationAuth.refreshIV && integrationAuth.refreshTag) {
|
||||
const refreshToken = decryptSymmetric128BitHexKeyUTF8({
|
||||
key: botKey,
|
||||
ciphertext: integrationAuth.refreshCiphertext,
|
||||
@@ -287,11 +243,7 @@ export const integrationAuthServiceFactory = ({
|
||||
}
|
||||
if (!accessToken) throw new BadRequestError({ message: "Missing access token" });
|
||||
|
||||
if (
|
||||
integrationAuth.accessIdTag &&
|
||||
integrationAuth.accessIdIV &&
|
||||
integrationAuth.accessIdCiphertext
|
||||
) {
|
||||
if (integrationAuth.accessIdTag && integrationAuth.accessIdIV && integrationAuth.accessIdCiphertext) {
|
||||
accessId = decryptSymmetric128BitHexKeyUTF8({
|
||||
key: botKey,
|
||||
ciphertext: integrationAuth.accessIdCiphertext,
|
||||
@@ -302,25 +254,12 @@ export const integrationAuthServiceFactory = ({
|
||||
return { accessId, accessToken };
|
||||
};
|
||||
|
||||
const getIntegrationApps = async ({
|
||||
actor,
|
||||
actorId,
|
||||
teamId,
|
||||
id,
|
||||
workspaceSlug
|
||||
}: TIntegrationAuthAppsDTO) => {
|
||||
const getIntegrationApps = async ({ actor, actorId, teamId, id, workspaceSlug }: TIntegrationAuthAppsDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken, accessId } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
@@ -339,15 +278,8 @@ export const integrationAuthServiceFactory = ({
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
@@ -359,24 +291,12 @@ export const integrationAuthServiceFactory = ({
|
||||
return teams;
|
||||
};
|
||||
|
||||
const getVercelBranches = async ({
|
||||
appId,
|
||||
id,
|
||||
actor,
|
||||
actorId
|
||||
}: TIntegrationAuthVercelBranchesDTO) => {
|
||||
const getVercelBranches = async ({ appId, id, actor, actorId }: TIntegrationAuthVercelBranchesDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
|
||||
@@ -399,37 +319,22 @@ export const integrationAuthServiceFactory = ({
|
||||
return [];
|
||||
};
|
||||
|
||||
const getChecklyGroups = async ({
|
||||
actorId,
|
||||
actor,
|
||||
id,
|
||||
accountId
|
||||
}: TIntegrationAuthChecklyGroupsDTO) => {
|
||||
const getChecklyGroups = async ({ actorId, actor, id, accountId }: TIntegrationAuthChecklyGroupsDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
if (accountId) {
|
||||
const { data } = await request.get<TChecklyGroups[]>(
|
||||
`${IntegrationUrls.CHECKLY_API_URL}/v1/check-groups`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json",
|
||||
"X-Checkly-Account": accountId
|
||||
}
|
||||
const { data } = await request.get<TChecklyGroups[]>(`${IntegrationUrls.CHECKLY_API_URL}/v1/check-groups`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json",
|
||||
"X-Checkly-Account": accountId
|
||||
}
|
||||
);
|
||||
});
|
||||
return data.map(({ name, id: groupId }) => ({ name, groupId }));
|
||||
}
|
||||
return [];
|
||||
@@ -439,15 +344,8 @@ export const integrationAuthServiceFactory = ({
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
const { data } = await request.get<{ results: Array<{ id: string; name: string }> }>(
|
||||
@@ -463,24 +361,12 @@ export const integrationAuthServiceFactory = ({
|
||||
return data.results.map(({ name, id: orgId }) => ({ name, orgId }));
|
||||
};
|
||||
|
||||
const getQoveryProjects = async ({
|
||||
actorId,
|
||||
actor,
|
||||
id,
|
||||
orgId
|
||||
}: TIntegrationAuthQoveryProjectDTO) => {
|
||||
const getQoveryProjects = async ({ actorId, actor, id, orgId }: TIntegrationAuthQoveryProjectDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
if (orgId) {
|
||||
@@ -498,24 +384,12 @@ export const integrationAuthServiceFactory = ({
|
||||
return [];
|
||||
};
|
||||
|
||||
const getQoveryEnvs = async ({
|
||||
projectId,
|
||||
id,
|
||||
actor,
|
||||
actorId
|
||||
}: TIntegrationAuthQoveryEnvironmentsDTO) => {
|
||||
const getQoveryEnvs = async ({ projectId, id, actor, actorId }: TIntegrationAuthQoveryEnvironmentsDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
if (projectId && projectId !== "none") {
|
||||
@@ -538,24 +412,12 @@ export const integrationAuthServiceFactory = ({
|
||||
return [];
|
||||
};
|
||||
|
||||
const getQoveryApps = async ({
|
||||
id,
|
||||
actor,
|
||||
actorId,
|
||||
environmentId
|
||||
}: TIntegrationAuthQoveryScopesDTO) => {
|
||||
const getQoveryApps = async ({ id, actor, actorId, environmentId }: TIntegrationAuthQoveryScopesDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
if (environmentId) {
|
||||
@@ -577,24 +439,12 @@ export const integrationAuthServiceFactory = ({
|
||||
return [];
|
||||
};
|
||||
|
||||
const getQoveryContainers = async ({
|
||||
id,
|
||||
actor,
|
||||
actorId,
|
||||
environmentId
|
||||
}: TIntegrationAuthQoveryScopesDTO) => {
|
||||
const getQoveryContainers = async ({ id, actor, actorId, environmentId }: TIntegrationAuthQoveryScopesDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
if (environmentId) {
|
||||
@@ -616,24 +466,12 @@ export const integrationAuthServiceFactory = ({
|
||||
return [];
|
||||
};
|
||||
|
||||
const getQoveryJobs = async ({
|
||||
id,
|
||||
actor,
|
||||
actorId,
|
||||
environmentId
|
||||
}: TIntegrationAuthQoveryScopesDTO) => {
|
||||
const getQoveryJobs = async ({ id, actor, actorId, environmentId }: TIntegrationAuthQoveryScopesDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
if (environmentId) {
|
||||
@@ -655,24 +493,12 @@ export const integrationAuthServiceFactory = ({
|
||||
return [];
|
||||
};
|
||||
|
||||
const getRailwayEnvironments = async ({
|
||||
id,
|
||||
actor,
|
||||
actorId,
|
||||
appId
|
||||
}: TIntegrationAuthRailwayEnvDTO) => {
|
||||
const getRailwayEnvironments = async ({ id, actor, actorId, appId }: TIntegrationAuthRailwayEnvDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
if (appId) {
|
||||
@@ -721,24 +547,12 @@ export const integrationAuthServiceFactory = ({
|
||||
}
|
||||
return [];
|
||||
};
|
||||
const getRailwayServices = async ({
|
||||
id,
|
||||
actor,
|
||||
actorId,
|
||||
appId
|
||||
}: TIntegrationAuthRailwayServicesDTO) => {
|
||||
const getRailwayServices = async ({ id, actor, actorId, appId }: TIntegrationAuthRailwayServicesDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
if (appId) {
|
||||
@@ -806,23 +620,12 @@ export const integrationAuthServiceFactory = ({
|
||||
return [];
|
||||
};
|
||||
|
||||
const getBitbucketWorkspaces = async ({
|
||||
actorId,
|
||||
actor,
|
||||
id
|
||||
}: TIntegrationAuthBitbucketWorkspaceDTO) => {
|
||||
const getBitbucketWorkspaces = async ({ actorId, actor, id }: TIntegrationAuthBitbucketWorkspaceDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
const workspaces: TBitbucketWorkspace[] = [];
|
||||
@@ -834,12 +637,11 @@ export const integrationAuthServiceFactory = ({
|
||||
const { data }: { data: { values: TBitbucketWorkspace[]; next: string } } = await request.get(
|
||||
workspaceUrl,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (data?.values.length > 0) {
|
||||
data.values.forEach((workspace) => {
|
||||
@@ -856,24 +658,12 @@ export const integrationAuthServiceFactory = ({
|
||||
return workspaces;
|
||||
};
|
||||
|
||||
const getNorthFlankSecretGroups = async ({
|
||||
id,
|
||||
actor,
|
||||
actorId,
|
||||
appId
|
||||
}: TIntegrationAuthNorthflankSecretGroupDTO) => {
|
||||
const getNorthFlankSecretGroups = async ({ id, actor, actorId, appId }: TIntegrationAuthNorthflankSecretGroupDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
const secretGroups: { name: string; groupId: string }[] = [];
|
||||
@@ -923,41 +713,26 @@ export const integrationAuthServiceFactory = ({
|
||||
return secretGroups;
|
||||
};
|
||||
|
||||
const getTeamcityBuildConfigs = async ({
|
||||
appId,
|
||||
id,
|
||||
actorId,
|
||||
actor
|
||||
}: TGetIntegrationAuthTeamCityBuildConfigDTO) => {
|
||||
const getTeamcityBuildConfigs = async ({ appId, id, actorId, actor }: TGetIntegrationAuthTeamCityBuildConfigDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
||||
if (appId) {
|
||||
const {
|
||||
data: { buildType }
|
||||
} = await request.get<{ buildType: TTeamCityBuildConfig[] }>(
|
||||
`${integrationAuth.url}/app/rest/buildTypes`,
|
||||
{
|
||||
params: {
|
||||
locator: `project:${appId}`
|
||||
},
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
} = await request.get<{ buildType: TTeamCityBuildConfig[] }>(`${integrationAuth.url}/app/rest/buildTypes`, {
|
||||
params: {
|
||||
locator: `project:${appId}`
|
||||
},
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return buildType.map(({ name, id: buildConfigId }) => ({
|
||||
name,
|
||||
@@ -967,39 +742,20 @@ export const integrationAuthServiceFactory = ({
|
||||
return [];
|
||||
};
|
||||
|
||||
const deleteIntegrationAuths = async ({
|
||||
projectId,
|
||||
integration,
|
||||
actor,
|
||||
actorId
|
||||
}: TDeleteIntegrationAuthsDTO) => {
|
||||
const deleteIntegrationAuths = async ({ projectId, integration, actor, actorId }: TDeleteIntegrationAuthsDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
|
||||
|
||||
const integrations = await integrationAuthDAL.delete({ integration, projectId });
|
||||
return integrations;
|
||||
};
|
||||
|
||||
const deleteIntegrationAuthById = async ({
|
||||
id,
|
||||
actorId,
|
||||
actor
|
||||
}: TDeleteIntegrationAuthByIdDTO) => {
|
||||
const deleteIntegrationAuthById = async ({ id, actorId, actor }: TDeleteIntegrationAuthByIdDTO) => {
|
||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
|
||||
|
||||
const delIntegrationAuth = await integrationAuthDAL.transaction(async (tx) => {
|
||||
const doc = await integrationAuthDAL.deleteById(integrationAuth.id, tx);
|
||||
|
||||
@@ -26,9 +26,7 @@ import { BadRequestError } from "@app/lib/errors";
|
||||
|
||||
import { Integrations, IntegrationUrls } from "./integration-list";
|
||||
|
||||
const getSecretKeyValuePair = (
|
||||
secrets: Record<string, { value: string | null; comment?: string } | null>
|
||||
) =>
|
||||
const getSecretKeyValuePair = (secrets: Record<string, { value: string | null; comment?: string } | null>) =>
|
||||
Object.keys(secrets).reduce<Record<string, string | null | undefined>>((prev, key) => {
|
||||
// eslint-disable-next-line
|
||||
prev[key] = secrets?.[key] === null ? null : secrets?.[key]?.value;
|
||||
@@ -289,9 +287,7 @@ const syncSecretsAzureKeyVault = async ({
|
||||
return result;
|
||||
};
|
||||
|
||||
const getAzureKeyVaultSecrets = await paginateAzureKeyVaultSecrets(
|
||||
`${integration.app}/secrets?api-version=7.3`
|
||||
);
|
||||
const getAzureKeyVaultSecrets = await paginateAzureKeyVaultSecrets(`${integration.app}/secrets?api-version=7.3`);
|
||||
|
||||
let lastSlashIndex: number;
|
||||
const res = (
|
||||
@@ -301,14 +297,11 @@ const syncSecretsAzureKeyVault = async ({
|
||||
lastSlashIndex = getAzureKeyVaultSecret.id.lastIndexOf("/");
|
||||
}
|
||||
|
||||
const azureKeyVaultSecret = await request.get(
|
||||
`${getAzureKeyVaultSecret.id}?api-version=7.3`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`
|
||||
}
|
||||
const azureKeyVaultSecret = await request.get(`${getAzureKeyVaultSecret.id}?api-version=7.3`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
...azureKeyVaultSecret.data,
|
||||
@@ -701,11 +694,7 @@ const syncSecretsVercel = async ({
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
integration.targetEnvironment === "preview" &&
|
||||
secret.gitBranch &&
|
||||
integration.path !== secret.gitBranch
|
||||
) {
|
||||
if (integration.targetEnvironment === "preview" && secret.gitBranch && integration.path !== secret.gitBranch) {
|
||||
// case: secret on preview environment does not have same target git branch
|
||||
return false;
|
||||
}
|
||||
@@ -719,16 +708,13 @@ const syncSecretsVercel = async ({
|
||||
if (vercelSecret.type === "encrypted") {
|
||||
// case: secret is encrypted -> need to decrypt
|
||||
const decryptedSecret = (
|
||||
await request.get(
|
||||
`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${vercelSecret.id}`,
|
||||
{
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
await request.get(`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${vercelSecret.id}`, {
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
)
|
||||
})
|
||||
).data;
|
||||
|
||||
res[vercelSecret.key] = decryptedSecret;
|
||||
@@ -798,47 +784,36 @@ const syncSecretsVercel = async ({
|
||||
|
||||
// Sync/push new secrets
|
||||
if (newSecrets.length > 0) {
|
||||
await request.post(
|
||||
`${IntegrationUrls.VERCEL_API_URL}/v10/projects/${integration.app}/env`,
|
||||
newSecrets,
|
||||
{
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
await request.post(`${IntegrationUrls.VERCEL_API_URL}/v10/projects/${integration.app}/env`, newSecrets, {
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
for await (const secret of updateSecrets) {
|
||||
if (secret.type !== "sensitive") {
|
||||
const { id, ...updatedSecret } = secret;
|
||||
await request.patch(
|
||||
`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${id}`,
|
||||
updatedSecret,
|
||||
{
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for await (const secret of deleteSecrets) {
|
||||
await request.delete(
|
||||
`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`,
|
||||
{
|
||||
await request.patch(`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${id}`, updatedSecret, {
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for await (const secret of deleteSecrets) {
|
||||
await request.delete(`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, {
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1366,9 +1341,7 @@ const syncSecretsFlyio = async ({
|
||||
)
|
||||
).data.data.app.secrets;
|
||||
|
||||
const deleteSecretsKeys = getSecretsRes
|
||||
.filter((secret) => !(secret.name in secrets))
|
||||
.map((secret) => secret.name);
|
||||
const deleteSecretsKeys = getSecretsRes.filter((secret) => !(secret.name in secrets)).map((secret) => secret.name);
|
||||
|
||||
// unset (delete) secrets
|
||||
const DeleteSecrets = `mutation($input: UnsetSecretsInput!) {
|
||||
@@ -1593,9 +1566,7 @@ const syncSecretsGitLab = async ({
|
||||
environment_scope: string;
|
||||
}
|
||||
|
||||
const gitLabApiUrl = integrationAuth.url
|
||||
? `${integrationAuth.url}/api`
|
||||
: IntegrationUrls.GITLAB_API_URL;
|
||||
const gitLabApiUrl = integrationAuth.url ? `${integrationAuth.url}/api` : IntegrationUrls.GITLAB_API_URL;
|
||||
|
||||
const getAllEnvVariables = async (integrationAppId: string, accToken: string) => {
|
||||
const headers = {
|
||||
@@ -1605,8 +1576,7 @@ const syncSecretsGitLab = async ({
|
||||
};
|
||||
|
||||
let allEnvVariables: GitLabSecret[] = [];
|
||||
let url: string | null =
|
||||
`${gitLabApiUrl}/v4/projects/${integrationAppId}/variables?per_page=100`;
|
||||
let url: string | null = `${gitLabApiUrl}/v4/projects/${integrationAppId}/variables?per_page=100`;
|
||||
|
||||
while (url) {
|
||||
const response = await request.get(url, { headers });
|
||||
@@ -1740,29 +1710,21 @@ const syncSecretsSupabase = async ({
|
||||
if (
|
||||
!(secretObj.name in secrets) &&
|
||||
// supbase reserved secret ref: https://supabase.com/docs/guides/functions/secrets#default-secrets
|
||||
![
|
||||
"SUPABASE_ANON_KEY",
|
||||
"SUPABASE_SERVICE_ROLE_KEY",
|
||||
"SUPABASE_DB_URL",
|
||||
"SUPABASE_URL"
|
||||
].includes(secretObj.name)
|
||||
!["SUPABASE_ANON_KEY", "SUPABASE_SERVICE_ROLE_KEY", "SUPABASE_DB_URL", "SUPABASE_URL"].includes(secretObj.name)
|
||||
) {
|
||||
return secretObj.name;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
await request.delete(
|
||||
`${IntegrationUrls.SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
"Accept-Encoding": "application/json"
|
||||
},
|
||||
data: secretsToDelete
|
||||
}
|
||||
);
|
||||
await request.delete(`${IntegrationUrls.SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
"Accept-Encoding": "application/json"
|
||||
},
|
||||
data: secretsToDelete
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1841,16 +1803,13 @@ const syncSecretsCheckly = async ({
|
||||
// sync secrets to checkly global envars
|
||||
|
||||
let getSecretsRes = (
|
||||
await request.get<{ key: string; value: string }[]>(
|
||||
`${IntegrationUrls.CHECKLY_API_URL}/v1/variables`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json",
|
||||
"X-Checkly-Account": integration.appId
|
||||
}
|
||||
await request.get<{ key: string; value: string }[]>(`${IntegrationUrls.CHECKLY_API_URL}/v1/variables`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json",
|
||||
"X-Checkly-Account": integration.appId
|
||||
}
|
||||
)
|
||||
})
|
||||
).data.reduce(
|
||||
(obj, secret) => ({
|
||||
...obj,
|
||||
@@ -2260,15 +2219,12 @@ const syncSecretsTeamCity = async ({
|
||||
for await (const key of Object.keys(res)) {
|
||||
if (!(key in secrets)) {
|
||||
// delete secret
|
||||
await request.delete(
|
||||
`${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters/env.${key}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
await request.delete(`${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters/env.${key}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2353,15 +2309,12 @@ const syncSecretsCloudflarePages = async ({
|
||||
const getSecretsRes = (
|
||||
await request.get<{
|
||||
result: { deployment_configs: Record<string, { env_vars: Record<string, unknown> }> };
|
||||
}>(
|
||||
`${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
}>(`${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
)
|
||||
})
|
||||
).data.result.deployment_configs[integration.targetEnvironment as string].env_vars;
|
||||
|
||||
// copy the secrets object, so we can set deleted keys to null
|
||||
@@ -2420,10 +2373,7 @@ const syncSecretsCloudflareWorkers = async ({
|
||||
).data.result;
|
||||
|
||||
const secretsObj = Object.fromEntries(
|
||||
Object.entries(getSecretKeyValuePair(secrets)).map(([key, val]) => [
|
||||
key,
|
||||
{ type: "secret_text", value: val }
|
||||
])
|
||||
Object.entries(getSecretKeyValuePair(secrets)).map(([key, val]) => [key, { type: "secret_text", value: val }])
|
||||
);
|
||||
|
||||
// get deleted secrets list
|
||||
@@ -2463,13 +2413,11 @@ const syncSecretsCloudflareWorkers = async ({
|
||||
};
|
||||
}
|
||||
|
||||
const data: ConvertedSecret[] = Object.entries(secretsObj as SecretsObj).map(
|
||||
([name, secret]) => ({
|
||||
name,
|
||||
text: secret.value,
|
||||
type: "secret_text"
|
||||
})
|
||||
);
|
||||
const data: ConvertedSecret[] = Object.entries(secretsObj as SecretsObj).map(([name, secret]) => ({
|
||||
name,
|
||||
text: secret.value,
|
||||
type: "secret_text"
|
||||
}));
|
||||
|
||||
await Promise.all(
|
||||
data.map(async (secret) => {
|
||||
@@ -2697,15 +2645,12 @@ const syncSecretsWindmill = async ({
|
||||
|
||||
// get secrets stored in windmill workspace
|
||||
const res = (
|
||||
await request.get<WindmillSecret[]>(
|
||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/list`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
await request.get<WindmillSecret[]>(`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/list`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
)
|
||||
})
|
||||
).data.reduce(
|
||||
(obj, secret) => ({
|
||||
...obj,
|
||||
@@ -2858,15 +2803,12 @@ const syncSecretsCloud66 = async ({
|
||||
for await (const key of Object.keys(res)) {
|
||||
if (!(key in secrets)) {
|
||||
// delete secret
|
||||
await request.delete(
|
||||
`${IntegrationUrls.CLOUD_66_API_URL}/3/stacks/${integration.appId}/environments/${key}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
await request.delete(`${IntegrationUrls.CLOUD_66_API_URL}/3/stacks/${integration.appId}/environments/${key}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -2920,8 +2862,7 @@ const syncSecretsHasuraCloud = async ({
|
||||
const res = await request.post(
|
||||
IntegrationUrls.HASURA_CLOUD_API_URL,
|
||||
{
|
||||
query:
|
||||
"query MyQuery($tenantId: uuid!) { getTenantEnv(tenantId: $tenantId) { hash envVars } }",
|
||||
query: "query MyQuery($tenantId: uuid!) { getTenantEnv(tenantId: $tenantId) { hash envVars } }",
|
||||
variables: {
|
||||
tenantId: integration.appId
|
||||
}
|
||||
|
||||
@@ -518,13 +518,7 @@ const exchangeRefreshHeroku = async ({ refreshToken }: { refreshToken: string })
|
||||
* @param {String} obj.refreshToken - refresh token to use to get new access token for GitLab
|
||||
* @returns
|
||||
*/
|
||||
const exchangeRefreshGitLab = async ({
|
||||
refreshToken,
|
||||
url
|
||||
}: {
|
||||
url?: string | null;
|
||||
refreshToken: string;
|
||||
}) => {
|
||||
const exchangeRefreshGitLab = async ({ refreshToken, url }: { url?: string | null; refreshToken: string }) => {
|
||||
const accessExpiresAt = new Date();
|
||||
const appCfg = getConfig();
|
||||
if (!appCfg.CLIENT_ID_GITLAB || !appCfg.CLIENT_SECRET_GITLAB) {
|
||||
@@ -628,19 +622,18 @@ const exchangeRefreshGCPSecretManager = async ({
|
||||
|
||||
const token = jwt.sign(payload, serviceAccount.private_key, { algorithm: "RS256" });
|
||||
|
||||
const { data }: { data: ServiceAccountAccessTokenGCPSecretManagerResponse } =
|
||||
await request.post(
|
||||
IntegrationUrls.GCP_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
assertion: token
|
||||
}).toString(),
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
}
|
||||
const { data }: { data: ServiceAccountAccessTokenGCPSecretManagerResponse } = await request.post(
|
||||
IntegrationUrls.GCP_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
assertion: token
|
||||
}).toString(),
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in);
|
||||
|
||||
|
||||
@@ -66,11 +66,7 @@ export const integrationDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const integrations = await (tx || db)(TableName.Integration)
|
||||
.where(`${TableName.Environment}.projectId`, projectId)
|
||||
.join(
|
||||
TableName.Environment,
|
||||
`${TableName.Integration}.envId`,
|
||||
`${TableName.Environment}.id`
|
||||
)
|
||||
.join(TableName.Environment, `${TableName.Integration}.envId`, `${TableName.Environment}.id`)
|
||||
.select(db.ref("name").withSchema(TableName.Environment).as("envName"))
|
||||
.select(db.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||
.select(db.ref("id").withSchema(TableName.Environment).as("envId"))
|
||||
@@ -99,11 +95,7 @@ export const integrationDALFactory = (db: TDbClient) => {
|
||||
.where("isActive", true)
|
||||
.where(`${TableName.Environment}.slug`, environment)
|
||||
.join(TableName.Environment, `${TableName.Integration}.envId`, `${TableName.Environment}.id`)
|
||||
.join(
|
||||
TableName.IntegrationAuth,
|
||||
`${TableName.IntegrationAuth}.id`,
|
||||
`${TableName.Integration}.integrationAuthId`
|
||||
)
|
||||
.join(TableName.IntegrationAuth, `${TableName.IntegrationAuth}.id`, `${TableName.Integration}.integrationAuthId`)
|
||||
.select(db.ref("name").withSchema(TableName.Environment).as("envName"))
|
||||
.select(db.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||
.select(db.ref("id").withSchema(TableName.Environment).as("envId"))
|
||||
@@ -119,10 +111,7 @@ export const integrationDALFactory = (db: TDbClient) => {
|
||||
db.ref("refreshCiphertext").withSchema(TableName.IntegrationAuth).as("refreshCiphertextAu"),
|
||||
db.ref("refreshIV").withSchema(TableName.IntegrationAuth).as("refreshIVAu"),
|
||||
db.ref("refreshTag").withSchema(TableName.IntegrationAuth).as("refreshTagAu"),
|
||||
db
|
||||
.ref("accessIdCiphertext")
|
||||
.withSchema(TableName.IntegrationAuth)
|
||||
.as("accessIdCiphertextAu"),
|
||||
db.ref("accessIdCiphertext").withSchema(TableName.IntegrationAuth).as("accessIdCiphertextAu"),
|
||||
db.ref("accessIdIV").withSchema(TableName.IntegrationAuth).as("accessIdIVAu"),
|
||||
db.ref("accessIdTag").withSchema(TableName.IntegrationAuth).as("accessIdTagAu"),
|
||||
db.ref("accessIV").withSchema(TableName.IntegrationAuth).as("accessIVAu"),
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { TProjectPermission } from "@app/lib/types";
|
||||
|
||||
@@ -12,11 +9,7 @@ import { TIntegrationAuthDALFactory } from "../integration-auth/integration-auth
|
||||
import { TSecretQueueFactory } from "../secret/secret-queue";
|
||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||
import { TIntegrationDALFactory } from "./integration-dal";
|
||||
import {
|
||||
TCreateIntegrationDTO,
|
||||
TDeleteIntegrationDTO,
|
||||
TUpdateIntegrationDTO
|
||||
} from "./integration-types";
|
||||
import { TCreateIntegrationDTO, TDeleteIntegrationDTO, TUpdateIntegrationDTO } from "./integration-types";
|
||||
|
||||
type TIntegrationServiceFactoryDep = {
|
||||
integrationDAL: TIntegrationDALFactory;
|
||||
@@ -57,21 +50,10 @@ export const integrationServiceFactory = ({
|
||||
const integrationAuth = await integrationAuthDAL.findById(integrationAuthId);
|
||||
if (!integrationAuth) throw new BadRequestError({ message: "Integration auth not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integrationAuth.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
|
||||
|
||||
const folder = await folderDAL.findBySecretPath(
|
||||
integrationAuth.projectId,
|
||||
sourceEnvironment,
|
||||
secretPath
|
||||
);
|
||||
const folder = await folderDAL.findBySecretPath(integrationAuth.projectId, sourceEnvironment, secretPath);
|
||||
if (!folder) throw new BadRequestError({ message: "Folder path not found" });
|
||||
|
||||
const integration = await integrationDAL.create({
|
||||
@@ -116,15 +98,8 @@ export const integrationServiceFactory = ({
|
||||
const integration = await integrationDAL.findById(id);
|
||||
if (!integration) throw new BadRequestError({ message: "Integration auth not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integration.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integration.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations);
|
||||
|
||||
const folder = await folderDAL.findBySecretPath(integration.projectId, environment, secretPath);
|
||||
if (!folder) throw new BadRequestError({ message: "Folder path not found" });
|
||||
@@ -146,15 +121,8 @@ export const integrationServiceFactory = ({
|
||||
const integration = await integrationDAL.findById(id);
|
||||
if (!integration) throw new BadRequestError({ message: "Integration auth not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
integration.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, integration.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
|
||||
|
||||
const deletedIntegration = await integrationDAL.deleteById(id);
|
||||
return { ...integration, ...deletedIntegration };
|
||||
@@ -162,10 +130,7 @@ export const integrationServiceFactory = ({
|
||||
|
||||
const listIntegrationByProject = async ({ actor, actorId, projectId }: TProjectPermission) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
|
||||
const integrations = await integrationDAL.findByProjectId(projectId);
|
||||
return integrations;
|
||||
|
||||
@@ -7,9 +7,7 @@ export type TIncidentContactsDALFactory = ReturnType<typeof incidentContactDALFa
|
||||
export const incidentContactDALFactory = (db: TDbClient) => {
|
||||
const create = async (orgId: string, email: string) => {
|
||||
try {
|
||||
const [incidentContact] = await db(TableName.IncidentContact)
|
||||
.insert({ orgId, email })
|
||||
.returning("*");
|
||||
const [incidentContact] = await db(TableName.IncidentContact).insert({ orgId, email }).returning("*");
|
||||
return incidentContact;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ name: "Incident contact create", error });
|
||||
@@ -38,10 +36,7 @@ export const incidentContactDALFactory = (db: TDbClient) => {
|
||||
|
||||
const deleteById = async (id: string, orgId: string) => {
|
||||
try {
|
||||
const [incidentContact] = await db(TableName.IncidentContact)
|
||||
.where({ orgId, id })
|
||||
.delete()
|
||||
.returning("*");
|
||||
const [incidentContact] = await db(TableName.IncidentContact).where({ orgId, id }).delete().returning("*");
|
||||
return incidentContact;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ name: "Incident contact delete", error });
|
||||
|
||||
@@ -11,13 +11,7 @@ import {
|
||||
TUserEncryptionKeys
|
||||
} from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import {
|
||||
buildFindFilter,
|
||||
selectAllTableCols,
|
||||
TFindFilter,
|
||||
TFindOpt,
|
||||
withTransaction
|
||||
} from "@app/lib/knex";
|
||||
import { buildFindFilter, selectAllTableCols, TFindFilter, TFindOpt, withTransaction } from "@app/lib/knex";
|
||||
|
||||
export type TOrgDALFactory = ReturnType<typeof orgDALFactory>;
|
||||
|
||||
@@ -36,11 +30,7 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const org = await db(TableName.OrgMembership)
|
||||
.where({ userId })
|
||||
.join(
|
||||
TableName.Organization,
|
||||
`${TableName.OrgMembership}.orgId`,
|
||||
`${TableName.Organization}.id`
|
||||
)
|
||||
.join(TableName.Organization, `${TableName.OrgMembership}.orgId`, `${TableName.Organization}.id`)
|
||||
.select(selectAllTableCols(TableName.Organization));
|
||||
return org;
|
||||
} catch (error) {
|
||||
@@ -105,10 +95,7 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
|
||||
const deleteById = async (orgId: string, tx?: Knex) => {
|
||||
try {
|
||||
const [org] = await (tx || db)(TableName.Organization)
|
||||
.where({ id: orgId })
|
||||
.delete()
|
||||
.returning("*");
|
||||
const [org] = await (tx || db)(TableName.Organization).where({ id: orgId }).delete().returning("*");
|
||||
return org;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Update organization" });
|
||||
@@ -142,26 +129,16 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
|
||||
const updateMembershipById = async (id: string, data: TOrgMembershipsUpdate, tx?: Knex) => {
|
||||
try {
|
||||
const [membership] = await (tx || db)(TableName.OrgMembership)
|
||||
.where({ id })
|
||||
.update(data)
|
||||
.returning("*");
|
||||
const [membership] = await (tx || db)(TableName.OrgMembership).where({ id }).update(data).returning("*");
|
||||
return membership;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Update org membership" });
|
||||
}
|
||||
};
|
||||
|
||||
const updateMembership = async (
|
||||
filter: Partial<TOrgMemberships>,
|
||||
data: TOrgMembershipsUpdate,
|
||||
tx?: Knex
|
||||
) => {
|
||||
const updateMembership = async (filter: Partial<TOrgMemberships>, data: TOrgMembershipsUpdate, tx?: Knex) => {
|
||||
try {
|
||||
const membership = await (tx || db)(TableName.OrgMembership)
|
||||
.where(filter)
|
||||
.update(data)
|
||||
.returning("*");
|
||||
const membership = await (tx || db)(TableName.OrgMembership).where(filter).update(data).returning("*");
|
||||
return membership;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Update org memberships" });
|
||||
@@ -170,10 +147,7 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
|
||||
const deleteMembershipById = async (id: string, orgId: string, tx?: Knex) => {
|
||||
try {
|
||||
const [membership] = await (tx || db)(TableName.OrgMembership)
|
||||
.where({ id, orgId })
|
||||
.delete()
|
||||
.returning("*");
|
||||
const [membership] = await (tx || db)(TableName.OrgMembership).where({ id, orgId }).delete().returning("*");
|
||||
return membership;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Delete org membership" });
|
||||
@@ -189,16 +163,11 @@ export const orgDALFactory = (db: TDbClient) => {
|
||||
// eslint-disable-next-line
|
||||
.where(buildFindFilter(filter))
|
||||
.join(TableName.Users, `${TableName.Users}.id`, `${TableName.OrgMembership}.userId`)
|
||||
.select(
|
||||
selectAllTableCols(TableName.OrgMembership),
|
||||
db.ref("email").withSchema(TableName.Users)
|
||||
);
|
||||
.select(selectAllTableCols(TableName.OrgMembership), db.ref("email").withSchema(TableName.Users));
|
||||
if (limit) void query.limit(limit);
|
||||
if (offset) void query.offset(offset);
|
||||
if (sort) {
|
||||
void query.orderBy(
|
||||
sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls }))
|
||||
);
|
||||
void query.orderBy(sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls })));
|
||||
}
|
||||
const res = await query;
|
||||
return res;
|
||||
|
||||
@@ -21,20 +21,10 @@ type TOrgRoleServiceFactoryDep = {
|
||||
|
||||
export type TOrgRoleServiceFactory = ReturnType<typeof orgRoleServiceFactory>;
|
||||
|
||||
export const orgRoleServiceFactory = ({
|
||||
orgRoleDAL,
|
||||
permissionService
|
||||
}: TOrgRoleServiceFactoryDep) => {
|
||||
const createRole = async (
|
||||
userId: string,
|
||||
orgId: string,
|
||||
data: Omit<TOrgRolesInsert, "orgId">
|
||||
) => {
|
||||
export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRoleServiceFactoryDep) => {
|
||||
const createRole = async (userId: string, orgId: string, data: Omit<TOrgRolesInsert, "orgId">) => {
|
||||
const { permission } = await permissionService.getUserOrgPermission(userId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Role
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Role);
|
||||
const existingRole = await orgRoleDAL.findOne({ slug: data.slug, orgId });
|
||||
if (existingRole) throw new BadRequestError({ name: "Create Role", message: "Duplicate role" });
|
||||
const role = await orgRoleDAL.create({
|
||||
@@ -45,17 +35,9 @@ export const orgRoleServiceFactory = ({
|
||||
return role;
|
||||
};
|
||||
|
||||
const updateRole = async (
|
||||
userId: string,
|
||||
orgId: string,
|
||||
roleId: string,
|
||||
data: Omit<TOrgRolesUpdate, "orgId">
|
||||
) => {
|
||||
const updateRole = async (userId: string, orgId: string, roleId: string, data: Omit<TOrgRolesUpdate, "orgId">) => {
|
||||
const { permission } = await permissionService.getUserOrgPermission(userId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.Role
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Role);
|
||||
if (data?.slug) {
|
||||
const existingRole = await orgRoleDAL.findOne({ slug: data.slug, orgId });
|
||||
if (existingRole && existingRole.id !== roleId)
|
||||
@@ -71,10 +53,7 @@ export const orgRoleServiceFactory = ({
|
||||
|
||||
const deleteRole = async (userId: string, orgId: string, roleId: string) => {
|
||||
const { permission } = await permissionService.getUserOrgPermission(userId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Delete,
|
||||
OrgPermissionSubjects.Role
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Role);
|
||||
const [deletedRole] = await orgRoleDAL.delete({ id: roleId, orgId });
|
||||
if (!deleteRole) throw new BadRequestError({ message: "Role not found", name: "Update role" });
|
||||
|
||||
@@ -83,10 +62,7 @@ export const orgRoleServiceFactory = ({
|
||||
|
||||
const listRoles = async (userId: string, orgId: string) => {
|
||||
const { permission } = await permissionService.getUserOrgPermission(userId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Role
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Role);
|
||||
const customRoles = await orgRoleDAL.find({ orgId });
|
||||
const roles = [
|
||||
{
|
||||
|
||||
@@ -5,10 +5,7 @@ import jwt from "jsonwebtoken";
|
||||
import { OrgMembershipRole, OrgMembershipStatus } from "@app/db/schemas";
|
||||
import { TProjects } from "@app/db/schemas/projects";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects
|
||||
} from "@app/ee/services/permission/org-permission";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { TSamlConfigDALFactory } from "@app/ee/services/saml-config/saml-config-dal";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
@@ -74,8 +71,7 @@ export const orgServiceFactory = ({
|
||||
const findOrganizationById = async (userId: string, orgId: string) => {
|
||||
await permissionService.getUserOrgPermission(userId, orgId);
|
||||
const org = await orgDAL.findOrgById(orgId);
|
||||
if (!org)
|
||||
throw new BadRequestError({ name: "Org not found", message: "Organization not found" });
|
||||
if (!org) throw new BadRequestError({ name: "Org not found", message: "Organization not found" });
|
||||
return org;
|
||||
};
|
||||
/*
|
||||
@@ -90,10 +86,7 @@ export const orgServiceFactory = ({
|
||||
* */
|
||||
const findAllOrgMembers = async (userId: string, orgId: string) => {
|
||||
const { permission } = await permissionService.getUserOrgPermission(userId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Member
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Member);
|
||||
|
||||
const members = await orgDAL.findAllOrgMembers(orgId);
|
||||
return members;
|
||||
@@ -101,14 +94,9 @@ export const orgServiceFactory = ({
|
||||
|
||||
const findAllWorkspaces = async ({ actor, actorId, orgId }: TFindAllWorkspacesDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.Workspace
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Workspace);
|
||||
|
||||
const organizationWorkspaceIds = new Set(
|
||||
(await projectDAL.find({ orgId })).map((workspace) => workspace.id)
|
||||
);
|
||||
const organizationWorkspaceIds = new Set((await projectDAL.find({ orgId })).map((workspace) => workspace.id));
|
||||
|
||||
let workspaces: (TProjects & { organization: string } & {
|
||||
environments: {
|
||||
@@ -134,13 +122,9 @@ export const orgServiceFactory = ({
|
||||
* */
|
||||
const updateOrgName = async (userId: string, orgId: string, name: string) => {
|
||||
const { permission } = await permissionService.getUserOrgPermission(userId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.Settings
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
|
||||
const org = await orgDAL.updateById(orgId, { name });
|
||||
if (!org)
|
||||
throw new BadRequestError({ name: "Org not found", message: "Organization not found" });
|
||||
if (!org) throw new BadRequestError({ name: "Org not found", message: "Organization not found" });
|
||||
return org;
|
||||
};
|
||||
/*
|
||||
@@ -222,29 +206,19 @@ export const orgServiceFactory = ({
|
||||
* Org membership management
|
||||
* Not another service because it has close ties with how an org works doesn't make sense to seperate them
|
||||
* */
|
||||
const updateOrgMembership = async ({
|
||||
role,
|
||||
orgId,
|
||||
userId,
|
||||
membershipId
|
||||
}: TUpdateOrgMembershipDTO) => {
|
||||
const updateOrgMembership = async ({ role, orgId, userId, membershipId }: TUpdateOrgMembershipDTO) => {
|
||||
const { permission } = await permissionService.getUserOrgPermission(userId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Edit,
|
||||
OrgPermissionSubjects.Member
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Member);
|
||||
|
||||
const isCustomRole = !Object.values(OrgMembershipRole).includes(role as OrgMembershipRole);
|
||||
if (isCustomRole) {
|
||||
const customRole = await orgRoleDAL.findOne({ slug: role, orgId });
|
||||
if (!customRole)
|
||||
throw new BadRequestError({ name: "Update membership", message: "Role not found" });
|
||||
if (!customRole) throw new BadRequestError({ name: "Update membership", message: "Role not found" });
|
||||
|
||||
const plan = await licenseService.getPlan(orgId);
|
||||
if (!plan?.rbac)
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member."
|
||||
message: "Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member."
|
||||
});
|
||||
|
||||
const [membership] = await orgDAL.updateMembership(
|
||||
@@ -257,10 +231,7 @@ export const orgServiceFactory = ({
|
||||
return membership;
|
||||
}
|
||||
|
||||
const [membership] = await orgDAL.updateMembership(
|
||||
{ id: membershipId, orgId },
|
||||
{ role, roleId: null }
|
||||
);
|
||||
const [membership] = await orgDAL.updateMembership({ id: membershipId, orgId }, { role, roleId: null });
|
||||
return membership;
|
||||
};
|
||||
/*
|
||||
@@ -268,10 +239,7 @@ export const orgServiceFactory = ({
|
||||
*/
|
||||
const inviteUserToOrganization = async ({ orgId, userId, inviteeEmail }: TInviteUserToOrgDTO) => {
|
||||
const { permission } = await permissionService.getUserOrgPermission(userId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Member
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Member);
|
||||
|
||||
const samlCfg = await samlConfigDAL.findOne({ orgId });
|
||||
if (samlCfg && samlCfg.isActive) {
|
||||
@@ -284,8 +252,7 @@ export const orgServiceFactory = ({
|
||||
// case: limit imposed on number of members allowed
|
||||
// case: number of members used exceeds the number of members allowed
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Failed to invite member due to member limit reached. Upgrade plan to invite more members."
|
||||
message: "Failed to invite member due to member limit reached. Upgrade plan to invite more members."
|
||||
});
|
||||
}
|
||||
const invitee = await orgDAL.transaction(async (tx) => {
|
||||
@@ -293,10 +260,7 @@ export const orgServiceFactory = ({
|
||||
if (inviteeUser) {
|
||||
// if user already exist means its already part of infisical
|
||||
// Thus the signup flow is not needed anymore
|
||||
const [inviteeMembership] = await orgDAL.findMembership(
|
||||
{ orgId, userId: inviteeUser.id },
|
||||
{ tx }
|
||||
);
|
||||
const [inviteeMembership] = await orgDAL.findMembership({ orgId, userId: inviteeUser.id }, { tx });
|
||||
if (inviteeMembership && inviteeMembership.status === OrgMembershipStatus.Accepted) {
|
||||
throw new BadRequestError({
|
||||
message: "Failed to invite an existing member of org",
|
||||
@@ -432,10 +396,7 @@ export const orgServiceFactory = ({
|
||||
|
||||
const deleteOrgMembership = async ({ orgId, userId, membershipId }: TDeleteOrgMembershipDTO) => {
|
||||
const { permission } = await permissionService.getUserOrgPermission(userId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Delete,
|
||||
OrgPermissionSubjects.Member
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Member);
|
||||
|
||||
const membership = await orgDAL.deleteMembershipById(membershipId, orgId);
|
||||
|
||||
@@ -448,20 +409,14 @@ export const orgServiceFactory = ({
|
||||
* */
|
||||
const findIncidentContacts = async (userId: string, orgId: string) => {
|
||||
const { permission } = await permissionService.getUserOrgPermission(userId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Read,
|
||||
OrgPermissionSubjects.IncidentAccount
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.IncidentAccount);
|
||||
const incidentContacts = await incidentContactDAL.findByOrgId(orgId);
|
||||
return incidentContacts;
|
||||
};
|
||||
|
||||
const createIncidentContact = async (userId: string, orgId: string, email: string) => {
|
||||
const { permission } = await permissionService.getUserOrgPermission(userId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.IncidentAccount
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.IncidentAccount);
|
||||
const doesIncidentContactExist = await incidentContactDAL.findOne(orgId, { email });
|
||||
if (doesIncidentContactExist) {
|
||||
throw new BadRequestError({
|
||||
@@ -476,10 +431,7 @@ export const orgServiceFactory = ({
|
||||
|
||||
const deleteIncidentContact = async (userId: string, orgId: string, id: string) => {
|
||||
const { permission } = await permissionService.getUserOrgPermission(userId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Delete,
|
||||
OrgPermissionSubjects.IncidentAccount
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.IncidentAccount);
|
||||
|
||||
const incidentContact = await incidentContactDAL.deleteById(id, orgId);
|
||||
return incidentContact;
|
||||
|
||||
@@ -15,11 +15,7 @@ export const projectBotDALFactory = (db: TDbClient) => {
|
||||
const bot = await (tx || db)(TableName.ProjectBot)
|
||||
.where(filter)
|
||||
.leftJoin(TableName.Users, `${TableName.ProjectBot}.senderId`, `${TableName.Users}.id`)
|
||||
.leftJoin(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
`${TableName.Users}.id`
|
||||
)
|
||||
.leftJoin(TableName.UserEncryptionKey, `${TableName.UserEncryptionKey}.userId`, `${TableName.Users}.id`)
|
||||
.select(selectAllTableCols(TableName.ProjectBot))
|
||||
.select(db.ref("publicKey").withSchema(TableName.UserEncryptionKey).as("senderPubKey"))
|
||||
.first();
|
||||
|
||||
@@ -2,10 +2,7 @@ import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import {
|
||||
decryptAsymmetric,
|
||||
@@ -28,10 +25,7 @@ type TProjectBotServiceFactoryDep = {
|
||||
|
||||
export type TProjectBotServiceFactory = ReturnType<typeof projectBotServiceFactory>;
|
||||
|
||||
export const projectBotServiceFactory = ({
|
||||
projectBotDAL,
|
||||
permissionService
|
||||
}: TProjectBotServiceFactoryDep) => {
|
||||
export const projectBotServiceFactory = ({ projectBotDAL, permissionService }: TProjectBotServiceFactoryDep) => {
|
||||
const getBotKey = async (projectId: string) => {
|
||||
const appCfg = getConfig();
|
||||
const encryptionKey = appCfg.ENCRYPTION_KEY;
|
||||
@@ -79,10 +73,7 @@ export const projectBotServiceFactory = ({
|
||||
|
||||
const findBotByProjectId = async ({ actorId, actor, projectId }: TProjectPermission) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||
const appCfg = getConfig();
|
||||
|
||||
const bot = await projectBotDAL.transaction(async (tx) => {
|
||||
@@ -108,10 +99,7 @@ export const projectBotServiceFactory = ({
|
||||
);
|
||||
}
|
||||
if (appCfg.ENCRYPTION_KEY) {
|
||||
const { iv, tag, ciphertext } = encryptSymmetric128BitHexKeyUTF8(
|
||||
privateKey,
|
||||
appCfg.ENCRYPTION_KEY
|
||||
);
|
||||
const { iv, tag, ciphertext } = encryptSymmetric128BitHexKeyUTF8(privateKey, appCfg.ENCRYPTION_KEY);
|
||||
return projectBotDAL.create(
|
||||
{
|
||||
name: "Infisical Bot",
|
||||
@@ -132,25 +120,12 @@ export const projectBotServiceFactory = ({
|
||||
return bot;
|
||||
};
|
||||
|
||||
const setBotActiveState = async ({
|
||||
actor,
|
||||
botId,
|
||||
botKey,
|
||||
actorId,
|
||||
isActive
|
||||
}: TSetActiveStateDTO) => {
|
||||
const setBotActiveState = async ({ actor, botId, botKey, actorId, isActive }: TSetActiveStateDTO) => {
|
||||
const bot = await projectBotDAL.findById(botId);
|
||||
if (!bot) throw new BadRequestError({ message: "Bot not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
bot.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.Integrations
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, bot.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations);
|
||||
|
||||
if (isActive) {
|
||||
if (!botKey?.nonce || !botKey?.encryptedKey) {
|
||||
|
||||
@@ -12,9 +12,7 @@ export const projectEnvDALFactory = (db: TDbClient) => {
|
||||
|
||||
const findBySlugs = async (projectId: string, env: string[], tx?: Knex) => {
|
||||
try {
|
||||
const envs = await (tx || db)(TableName.Environment)
|
||||
.where("projectId", projectId)
|
||||
.whereIn("slug", env);
|
||||
const envs = await (tx || db)(TableName.Environment).where("projectId", projectId).whereIn("slug", env);
|
||||
return envs;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find by slugs" });
|
||||
@@ -31,12 +29,7 @@ export const projectEnvDALFactory = (db: TDbClient) => {
|
||||
return lastPos?.position || 0;
|
||||
};
|
||||
|
||||
const updateAllPosition = async (
|
||||
projectId: string,
|
||||
pos: number,
|
||||
targetPos: number,
|
||||
tx?: Knex
|
||||
) => {
|
||||
const updateAllPosition = async (projectId: string, pos: number, targetPos: number, tx?: Knex) => {
|
||||
try {
|
||||
if (targetPos === -1) {
|
||||
// this means delete
|
||||
|
||||
@@ -2,10 +2,7 @@ import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
@@ -32,10 +29,7 @@ export const projectEnvServiceFactory = ({
|
||||
}: TProjectEnvServiceFactoryDep) => {
|
||||
const createEnvironment = async ({ projectId, actorId, actor, name, slug }: TCreateEnvDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Environments
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Environments);
|
||||
|
||||
const envs = await projectEnvDAL.find({ projectId });
|
||||
const existingEnv = envs.find(({ slug: envSlug }) => envSlug === slug);
|
||||
@@ -65,20 +59,9 @@ export const projectEnvServiceFactory = ({
|
||||
return env;
|
||||
};
|
||||
|
||||
const updateEnvironment = async ({
|
||||
projectId,
|
||||
slug,
|
||||
actor,
|
||||
actorId,
|
||||
name,
|
||||
id,
|
||||
position
|
||||
}: TUpdateEnvDTO) => {
|
||||
const updateEnvironment = async ({ projectId, slug, actor, actorId, name, id, position }: TUpdateEnvDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.Environments
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Environments);
|
||||
|
||||
const oldEnv = await projectEnvDAL.findOne({ id, projectId });
|
||||
if (!oldEnv) throw new BadRequestError({ message: "Environment not found" });
|
||||
@@ -104,10 +87,7 @@ export const projectEnvServiceFactory = ({
|
||||
|
||||
const deleteEnvironment = async ({ projectId, actor, actorId, id }: TDeleteEnvDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.Environments
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Environments);
|
||||
|
||||
const env = await projectEnvDAL.transaction(async (tx) => {
|
||||
const [doc] = await projectEnvDAL.delete({ id, projectId }, tx);
|
||||
|
||||
@@ -15,11 +15,7 @@ export const projectKeyDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const projectKey = await db(TableName.ProjectKeys)
|
||||
.join(TableName.Users, `${TableName.ProjectKeys}.senderId`, `${TableName.Users}.id`)
|
||||
.join(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
`${TableName.Users}.id`
|
||||
)
|
||||
.join(TableName.UserEncryptionKey, `${TableName.UserEncryptionKey}.userId`, `${TableName.Users}.id`)
|
||||
.where({ projectId, receiverId: userId })
|
||||
.orderBy("createdAt", "desc", "last")
|
||||
.select(selectAllTableCols(TableName.ProjectKeys))
|
||||
@@ -38,11 +34,7 @@ export const projectKeyDALFactory = (db: TDbClient) => {
|
||||
const pubKeys = await db(TableName.ProjectMembership)
|
||||
.where({ projectId })
|
||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
||||
.join(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.Users}.id`,
|
||||
`${TableName.UserEncryptionKey}.userId`
|
||||
)
|
||||
.join(TableName.UserEncryptionKey, `${TableName.Users}.id`, `${TableName.UserEncryptionKey}.userId`)
|
||||
.select("userId", "publicKey");
|
||||
return pubKeys;
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
|
||||
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
|
||||
@@ -33,10 +30,7 @@ export const projectKeyServiceFactory = ({
|
||||
encryptedKey
|
||||
}: TUploadProjectKeyDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.Member
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
|
||||
|
||||
const receiverMembership = await projectMembershipDAL.findOne({
|
||||
userId: receiverId,
|
||||
@@ -59,10 +53,7 @@ export const projectKeyServiceFactory = ({
|
||||
|
||||
const getProjectPublicKeys = async ({ actor, actorId, projectId }: TGetLatestProjectKeyDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Member
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
||||
return projectKeyDAL.findAllProjectUserPubKeys(projectId);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,10 +3,7 @@ import { ForbiddenError } from "@casl/ability";
|
||||
import { OrgMembershipStatus, ProjectMembershipRole, TableName } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
@@ -53,25 +50,14 @@ export const projectMembershipServiceFactory = ({
|
||||
}: TProjectMembershipServiceFactoryDep) => {
|
||||
const getProjectMemberships = async ({ actorId, actor, projectId }: TGetProjectMembershipDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Member
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
||||
|
||||
return projectMembershipDAL.findAllProjectMembers(projectId);
|
||||
};
|
||||
|
||||
const inviteUserToProject = async ({
|
||||
actorId,
|
||||
actor,
|
||||
projectId,
|
||||
email
|
||||
}: TInviteUserToProjectDTO) => {
|
||||
const inviteUserToProject = async ({ actorId, actor, projectId, email }: TInviteUserToProjectDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Member
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Member);
|
||||
|
||||
const invitee = await userDAL.findOne({ email });
|
||||
if (!invitee || !invitee.isAccepted)
|
||||
@@ -126,37 +112,25 @@ export const projectMembershipServiceFactory = ({
|
||||
return { invitee, latestKey };
|
||||
};
|
||||
|
||||
const addUsersToProject = async ({
|
||||
projectId,
|
||||
actorId,
|
||||
actor,
|
||||
members
|
||||
}: TAddUsersToWorkspaceDTO) => {
|
||||
const addUsersToProject = async ({ projectId, actorId, actor, members }: TAddUsersToWorkspaceDTO) => {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
if (!project) throw new BadRequestError({ message: "Project not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Member
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Member);
|
||||
const orgMembers = await orgDAL.findMembership({
|
||||
orgId: project.orgId,
|
||||
$in: {
|
||||
[`${TableName.OrgMembership}.id` as "id"]: members.map(
|
||||
({ orgMembershipId }) => orgMembershipId
|
||||
)
|
||||
[`${TableName.OrgMembership}.id` as "id"]: members.map(({ orgMembershipId }) => orgMembershipId)
|
||||
}
|
||||
});
|
||||
if (orgMembers.length !== members.length)
|
||||
throw new BadRequestError({ message: "Some users are not part of org" });
|
||||
if (orgMembers.length !== members.length) throw new BadRequestError({ message: "Some users are not part of org" });
|
||||
|
||||
const existingMembers = await projectMembershipDAL.find({
|
||||
projectId,
|
||||
$in: { userId: orgMembers.map(({ userId }) => userId).filter(Boolean) as string[] }
|
||||
});
|
||||
if (existingMembers.length)
|
||||
throw new BadRequestError({ message: "Some users are already part of project" });
|
||||
if (existingMembers.length) throw new BadRequestError({ message: "Some users are already part of project" });
|
||||
|
||||
await projectMembershipDAL.transaction(async (tx) => {
|
||||
await projectMembershipDAL.insertMany(
|
||||
@@ -203,24 +177,17 @@ export const projectMembershipServiceFactory = ({
|
||||
role
|
||||
}: TUpdateProjectMembershipDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.Member
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
|
||||
|
||||
const isCustomRole = !Object.values(ProjectMembershipRole).includes(
|
||||
role as ProjectMembershipRole
|
||||
);
|
||||
const isCustomRole = !Object.values(ProjectMembershipRole).includes(role as ProjectMembershipRole);
|
||||
if (isCustomRole) {
|
||||
const customRole = await projectRoleDAL.findOne({ slug: role, projectId });
|
||||
if (!customRole)
|
||||
throw new BadRequestError({ name: "Update project membership", message: "Role not found" });
|
||||
if (!customRole) throw new BadRequestError({ name: "Update project membership", message: "Role not found" });
|
||||
const project = await projectDAL.findById(customRole.projectId);
|
||||
const plan = await licenseService.getPlan(project.orgId);
|
||||
if (!plan?.rbac)
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member."
|
||||
message: "Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member."
|
||||
});
|
||||
|
||||
const [membership] = await projectMembershipDAL.update(
|
||||
@@ -233,30 +200,16 @@ export const projectMembershipServiceFactory = ({
|
||||
return membership;
|
||||
}
|
||||
|
||||
const [membership] = await projectMembershipDAL.update(
|
||||
{ id: membershipId, projectId },
|
||||
{ role, roleId: null }
|
||||
);
|
||||
const [membership] = await projectMembershipDAL.update({ id: membershipId, projectId }, { role, roleId: null });
|
||||
return membership;
|
||||
};
|
||||
|
||||
const deleteProjectMembership = async ({
|
||||
actorId,
|
||||
actor,
|
||||
projectId,
|
||||
membershipId
|
||||
}: TDeleteProjectMembershipDTO) => {
|
||||
const deleteProjectMembership = async ({ actorId, actor, projectId, membershipId }: TDeleteProjectMembershipDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.Member
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Member);
|
||||
|
||||
const membership = await projectMembershipDAL.transaction(async (tx) => {
|
||||
const [deletedMembership] = await projectMembershipDAL.delete(
|
||||
{ projectId, id: membershipId },
|
||||
tx
|
||||
);
|
||||
const [deletedMembership] = await projectMembershipDAL.delete({ projectId, id: membershipId }, tx);
|
||||
await projectKeyDAL.delete({ receiverId: deletedMembership.userId, projectId }, tx);
|
||||
return deletedMembership;
|
||||
});
|
||||
|
||||
@@ -18,18 +18,12 @@ import { TProjectRoleDALFactory } from "./project-role-dal";
|
||||
|
||||
type TProjectRoleServiceFactoryDep = {
|
||||
projectRoleDAL: TProjectRoleDALFactory;
|
||||
permissionService: Pick<
|
||||
TPermissionServiceFactory,
|
||||
"getProjectPermission" | "getUserProjectPermission"
|
||||
>;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getUserProjectPermission">;
|
||||
};
|
||||
|
||||
export type TProjectRoleServiceFactory = ReturnType<typeof projectRoleServiceFactory>;
|
||||
|
||||
export const projectRoleServiceFactory = ({
|
||||
projectRoleDAL,
|
||||
permissionService
|
||||
}: TProjectRoleServiceFactoryDep) => {
|
||||
export const projectRoleServiceFactory = ({ projectRoleDAL, permissionService }: TProjectRoleServiceFactoryDep) => {
|
||||
const createRole = async (
|
||||
actor: ActorType,
|
||||
actorId: string,
|
||||
@@ -37,10 +31,7 @@ export const projectRoleServiceFactory = ({
|
||||
data: Omit<TProjectRolesInsert, "projectId">
|
||||
) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Role
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Role);
|
||||
const existingRole = await projectRoleDAL.findOne({ slug: data.slug, projectId });
|
||||
if (existingRole) throw new BadRequestError({ name: "Create Role", message: "Duplicate role" });
|
||||
const role = await projectRoleDAL.create({
|
||||
@@ -59,10 +50,7 @@ export const projectRoleServiceFactory = ({
|
||||
data: Omit<TOrgRolesUpdate, "orgId">
|
||||
) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.Role
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Role);
|
||||
if (data?.slug) {
|
||||
const existingRole = await projectRoleDAL.findOne({ slug: data.slug, projectId });
|
||||
if (existingRole && existingRole.id !== roleId)
|
||||
@@ -76,17 +64,9 @@ export const projectRoleServiceFactory = ({
|
||||
return updatedRole;
|
||||
};
|
||||
|
||||
const deleteRole = async (
|
||||
actor: ActorType,
|
||||
actorId: string,
|
||||
projectId: string,
|
||||
roleId: string
|
||||
) => {
|
||||
const deleteRole = async (actor: ActorType, actorId: string, projectId: string, roleId: string) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.Role
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Role);
|
||||
const [deletedRole] = await projectRoleDAL.delete({ id: roleId, projectId });
|
||||
if (!deleteRole) throw new BadRequestError({ message: "Role not found", name: "Update role" });
|
||||
|
||||
@@ -95,10 +75,7 @@ export const projectRoleServiceFactory = ({
|
||||
|
||||
const listRoles = async (actor: ActorType, actorId: string, projectId: string) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Role
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
||||
const customRoles = await projectRoleDAL.find({ projectId });
|
||||
const roles = [
|
||||
{
|
||||
@@ -151,10 +128,7 @@ export const projectRoleServiceFactory = ({
|
||||
};
|
||||
|
||||
const getUserPermission = async (userId: string, projectId: string) => {
|
||||
const { permission, membership } = await permissionService.getUserProjectPermission(
|
||||
userId,
|
||||
projectId
|
||||
);
|
||||
const { permission, membership } = await permissionService.getUserProjectPermission(userId, projectId);
|
||||
return { permissions: packRules(permission.rules), membership };
|
||||
};
|
||||
|
||||
|
||||
@@ -12,16 +12,8 @@ export const projectDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const workspaces = await db(TableName.ProjectMembership)
|
||||
.where({ userId })
|
||||
.join(
|
||||
TableName.Project,
|
||||
`${TableName.ProjectMembership}.projectId`,
|
||||
`${TableName.Project}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.Environment,
|
||||
`${TableName.Environment}.projectId`,
|
||||
`${TableName.Project}.id`
|
||||
)
|
||||
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
.leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
|
||||
.select(
|
||||
selectAllTableCols(TableName.Project),
|
||||
db.ref("id").withSchema(TableName.Project).as("_id"),
|
||||
@@ -60,16 +52,8 @@ export const projectDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const workspaces = await db(TableName.IdentityProjectMembership)
|
||||
.where({ identityId })
|
||||
.join(
|
||||
TableName.Project,
|
||||
`${TableName.IdentityProjectMembership}.projectId`,
|
||||
`${TableName.Project}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.Environment,
|
||||
`${TableName.Environment}.projectId`,
|
||||
`${TableName.Project}.id`
|
||||
)
|
||||
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
.leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
|
||||
.select(
|
||||
selectAllTableCols(TableName.Project),
|
||||
db.ref("id").withSchema(TableName.Project).as("_id"),
|
||||
@@ -110,16 +94,8 @@ export const projectDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const workspaces = await db(TableName.ProjectMembership)
|
||||
.where(`${TableName.Project}.id`, id)
|
||||
.join(
|
||||
TableName.Project,
|
||||
`${TableName.ProjectMembership}.projectId`,
|
||||
`${TableName.Project}.id`
|
||||
)
|
||||
.join(
|
||||
TableName.Environment,
|
||||
`${TableName.Environment}.projectId`,
|
||||
`${TableName.Project}.id`
|
||||
)
|
||||
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
|
||||
.join(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
|
||||
.select(
|
||||
selectAllTableCols(TableName.Project),
|
||||
db.ref("id").withSchema(TableName.Project).as("_id"),
|
||||
|
||||
@@ -3,15 +3,9 @@ import slugify from "@sindresorhus/slugify";
|
||||
|
||||
import { ProjectMembershipRole } from "@app/db/schemas";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects
|
||||
} from "@app/ee/services/permission/org-permission";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { createSecretBlindIndex } from "@app/lib/crypto";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
@@ -56,10 +50,7 @@ export const projectServiceFactory = ({
|
||||
* */
|
||||
const createProject = async ({ orgId, actor, actorId, workspaceName }: TCreateProjectDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionActions.Create,
|
||||
OrgPermissionSubjects.Workspace
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
|
||||
|
||||
const appCfg = getConfig();
|
||||
const blindIndex = createSecretBlindIndex(appCfg.ROOT_ENCRYPTION_KEY, appCfg.ENCRYPTION_KEY);
|
||||
@@ -69,8 +60,7 @@ export const projectServiceFactory = ({
|
||||
// case: limit imposed on number of workspaces allowed
|
||||
// case: number of workspaces used exceeds the number of workspaces allowed
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces."
|
||||
message: "Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces."
|
||||
});
|
||||
}
|
||||
|
||||
@@ -118,10 +108,7 @@ export const projectServiceFactory = ({
|
||||
|
||||
const deleteProject = async ({ actor, actorId, projectId }: TDeleteProjectDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.Project
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Project);
|
||||
|
||||
const deletedProject = await projectDAL.deleteById(projectId);
|
||||
return deletedProject;
|
||||
@@ -144,26 +131,15 @@ export const projectServiceFactory = ({
|
||||
autoCapitalization
|
||||
}: TGetProjectDTO & { autoCapitalization: boolean }) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.Settings
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
|
||||
|
||||
const updatedProject = await projectDAL.updateById(projectId, { autoCapitalization });
|
||||
return updatedProject;
|
||||
};
|
||||
|
||||
const updateName = async ({
|
||||
projectId,
|
||||
actor,
|
||||
actorId,
|
||||
name
|
||||
}: TGetProjectDTO & { name: string }) => {
|
||||
const updateName = async ({ projectId, actor, actorId, name }: TGetProjectDTO & { name: string }) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.Settings
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
|
||||
|
||||
const updatedProject = await projectDAL.updateById(projectId, { name });
|
||||
return updatedProject;
|
||||
|
||||
@@ -13,16 +13,8 @@ export const secretBlindIndexDALFactory = (db: TDbClient) => {
|
||||
const countOfSecretsWithNullSecretBlindIndex = async (projectId: string, tx?: Knex) => {
|
||||
try {
|
||||
const doc = await (tx || db)(TableName.Secret)
|
||||
.leftJoin(
|
||||
TableName.SecretFolder,
|
||||
`${TableName.SecretFolder}.id`,
|
||||
`${TableName.Secret}.folderId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.Environment,
|
||||
`${TableName.Environment}.id`,
|
||||
`${TableName.SecretFolder}.envId`
|
||||
)
|
||||
.leftJoin(TableName.SecretFolder, `${TableName.SecretFolder}.id`, `${TableName.Secret}.folderId`)
|
||||
.leftJoin(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretFolder}.envId`)
|
||||
.where({ projectId })
|
||||
.whereNull("secretBlindIndex")
|
||||
.count(`${TableName.Secret}.id` as "id");
|
||||
@@ -35,16 +27,8 @@ export const secretBlindIndexDALFactory = (db: TDbClient) => {
|
||||
const findAllSecretsByProjectId = async (projectId: string, tx?: Knex) => {
|
||||
try {
|
||||
const docs = await (tx || db)(TableName.Secret)
|
||||
.leftJoin(
|
||||
TableName.SecretFolder,
|
||||
`${TableName.SecretFolder}.id`,
|
||||
`${TableName.Secret}.folderId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.Environment,
|
||||
`${TableName.Environment}.id`,
|
||||
`${TableName.SecretFolder}.envId`
|
||||
)
|
||||
.leftJoin(TableName.SecretFolder, `${TableName.SecretFolder}.id`, `${TableName.Secret}.folderId`)
|
||||
.leftJoin(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretFolder}.envId`)
|
||||
.where({ projectId })
|
||||
.whereNull("secretBlindIndex")
|
||||
.select(selectAllTableCols(TableName.Secret))
|
||||
@@ -61,16 +45,8 @@ export const secretBlindIndexDALFactory = (db: TDbClient) => {
|
||||
const findSecretsByProjectId = async (projectId: string, secretIds: string[], tx?: Knex) => {
|
||||
try {
|
||||
const docs = await (tx || db)(TableName.Secret)
|
||||
.leftJoin(
|
||||
TableName.SecretFolder,
|
||||
`${TableName.SecretFolder}.id`,
|
||||
`${TableName.Secret}.folderId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.Environment,
|
||||
`${TableName.Environment}.id`,
|
||||
`${TableName.SecretFolder}.envId`
|
||||
)
|
||||
.leftJoin(TableName.SecretFolder, `${TableName.SecretFolder}.id`, `${TableName.Secret}.folderId`)
|
||||
.leftJoin(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretFolder}.envId`)
|
||||
.where({ projectId })
|
||||
.whereIn(`${TableName.Secret}.id`, secretIds)
|
||||
.whereNull("secretBlindIndex")
|
||||
|
||||
@@ -24,11 +24,7 @@ export const secretBlindIndexServiceFactory = ({
|
||||
permissionService,
|
||||
secretDAL
|
||||
}: TSecretBlindIndexServiceFactoryDep) => {
|
||||
const getSecretBlindIndexStatus = async ({
|
||||
actor,
|
||||
projectId,
|
||||
actorId
|
||||
}: TGetProjectBlindIndexStatusDTO) => {
|
||||
const getSecretBlindIndexStatus = async ({ actor, projectId, actorId }: TGetProjectBlindIndexStatusDTO) => {
|
||||
await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
|
||||
const secretCount = await secretBlindIndexDAL.countOfSecretsWithNullSecretBlindIndex(projectId);
|
||||
@@ -57,15 +53,13 @@ export const secretBlindIndexServiceFactory = ({
|
||||
}
|
||||
|
||||
const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId });
|
||||
if (!blindIndexCfg)
|
||||
throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" });
|
||||
if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" });
|
||||
|
||||
const secrets = await secretBlindIndexDAL.findSecretsByProjectId(
|
||||
projectId,
|
||||
secretsToUpdate.map(({ secretId }) => secretId)
|
||||
);
|
||||
if (secrets.length !== secretsToUpdate.length)
|
||||
throw new BadRequestError({ message: "Secret not found" });
|
||||
if (secrets.length !== secretsToUpdate.length) throw new BadRequestError({ message: "Secret not found" });
|
||||
|
||||
const operations = await Promise.all(
|
||||
secretsToUpdate.map(async ({ secretName, secretId: id }) => {
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TDbClient } from "@app/db";
|
||||
import {
|
||||
TableName,
|
||||
TProjectEnvironments,
|
||||
TSecretFolders,
|
||||
TSecretFoldersUpdate
|
||||
} from "@app/db/schemas";
|
||||
import { TableName, TProjectEnvironments, TSecretFolders, TSecretFoldersUpdate } from "@app/db/schemas";
|
||||
import { BadRequestError, DatabaseError } from "@app/lib/errors";
|
||||
import { groupBy, removeTrailingSlash } from "@app/lib/fn";
|
||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||
@@ -16,14 +11,10 @@ export const validateFolderName = (folderName: string) => {
|
||||
return validNameRegex.test(folderName);
|
||||
};
|
||||
|
||||
const sqlFindMultipleFolderByEnvPathQuery = (
|
||||
db: Knex,
|
||||
query: Array<{ envId: string; secretPath: string }>
|
||||
) => {
|
||||
const sqlFindMultipleFolderByEnvPathQuery = (db: Knex, query: Array<{ envId: string; secretPath: string }>) => {
|
||||
// this is removing an trailing slash like /folder1/folder2/ -> /folder1/folder2
|
||||
const formatedQuery = query.map(({ envId, secretPath }) => {
|
||||
const formatedPath =
|
||||
secretPath.at(-1) === "/" && secretPath.length > 1 ? secretPath.slice(0, -1) : secretPath;
|
||||
const formatedPath = secretPath.at(-1) === "/" && secretPath.length > 1 ? secretPath.slice(0, -1) : secretPath;
|
||||
const segments = formatedPath.split("/").filter(Boolean);
|
||||
if (segments.some((segment) => !validateFolderName(segment))) {
|
||||
throw new BadRequestError({ message: "Invalid folder name" });
|
||||
@@ -73,9 +64,9 @@ const sqlFindMultipleFolderByEnvPathQuery = (
|
||||
.where((wb) =>
|
||||
formatedQuery.map(({ secretPath }) =>
|
||||
wb.orWhereRaw(
|
||||
`depth = array_position(ARRAY[${secretPath
|
||||
.map(() => "?")
|
||||
.join(",")}]::varchar[], ${TableName.SecretFolder}.name,depth)`,
|
||||
`depth = array_position(ARRAY[${secretPath.map(() => "?").join(",")}]::varchar[], ${
|
||||
TableName.SecretFolder
|
||||
}.name,depth)`,
|
||||
[...secretPath]
|
||||
)
|
||||
)
|
||||
@@ -92,15 +83,9 @@ const sqlFindMultipleFolderByEnvPathQuery = (
|
||||
.from<TSecretFolders & { depth: number; path: string }>("parent");
|
||||
};
|
||||
|
||||
const sqlFindFolderByPathQuery = (
|
||||
db: Knex,
|
||||
projectId: string,
|
||||
environment: string,
|
||||
secretPath: string
|
||||
) => {
|
||||
const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environment: string, secretPath: string) => {
|
||||
// this is removing an trailing slash like /folder1/folder2/ -> /folder1/folder2
|
||||
const formatedPath =
|
||||
secretPath.at(-1) === "/" && secretPath.length > 1 ? secretPath.slice(0, -1) : secretPath;
|
||||
const formatedPath = secretPath.at(-1) === "/" && secretPath.length > 1 ? secretPath.slice(0, -1) : secretPath;
|
||||
// next goal to sanitize saw the raw sql query is safe
|
||||
// for this we ensure folder name contains only string and - nothing else
|
||||
const pathSegments = formatedPath.split("/").filter(Boolean);
|
||||
@@ -121,11 +106,7 @@ const sqlFindFolderByPathQuery = (
|
||||
path: db.raw("'/'")
|
||||
})
|
||||
.from(TableName.SecretFolder)
|
||||
.join(
|
||||
TableName.Environment,
|
||||
`${TableName.SecretFolder}.envId`,
|
||||
`${TableName.Environment}.id`
|
||||
)
|
||||
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
|
||||
.where({
|
||||
projectId,
|
||||
parentId: null
|
||||
@@ -156,11 +137,7 @@ const sqlFindFolderByPathQuery = (
|
||||
);
|
||||
})
|
||||
.from<TSecretFolders & { depth: number; path: string }>("parent")
|
||||
.leftJoin<TProjectEnvironments>(
|
||||
TableName.Environment,
|
||||
`${TableName.Environment}.id`,
|
||||
"parent.envId"
|
||||
)
|
||||
.leftJoin<TProjectEnvironments>(TableName.Environment, `${TableName.Environment}.id`, "parent.envId")
|
||||
.select<
|
||||
TSecretFolders & {
|
||||
depth: number;
|
||||
@@ -192,16 +169,10 @@ const sqlFindSecretPathByFolderId = (db: Knex, projectId: string, folderIds: str
|
||||
// this is for root condition
|
||||
// if the given folder id is root folder id then intial path is set as / instead of /root
|
||||
// if not root folder the path here will be /<folder name>
|
||||
path: db.raw(
|
||||
`CONCAT('/', (CASE WHEN "parentId" is NULL THEN '' ELSE ${TableName.SecretFolder}.name END))`
|
||||
),
|
||||
path: db.raw(`CONCAT('/', (CASE WHEN "parentId" is NULL THEN '' ELSE ${TableName.SecretFolder}.name END))`),
|
||||
child: db.raw("NULL::uuid")
|
||||
})
|
||||
.join(
|
||||
TableName.Environment,
|
||||
`${TableName.SecretFolder}.envId`,
|
||||
`${TableName.Environment}.id`
|
||||
)
|
||||
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
|
||||
.where({ projectId })
|
||||
.whereIn(`${TableName.SecretFolder}.id`, folderIds)
|
||||
.union(
|
||||
@@ -234,19 +205,9 @@ export const ROOT_FOLDER_NAME = "root";
|
||||
export const secretFolderDALFactory = (db: TDbClient) => {
|
||||
const secretFolderOrm = ormify(db, TableName.SecretFolder);
|
||||
|
||||
const findBySecretPath = async (
|
||||
projectId: string,
|
||||
environment: string,
|
||||
path: string,
|
||||
tx?: Knex
|
||||
) => {
|
||||
const findBySecretPath = async (projectId: string, environment: string, path: string, tx?: Knex) => {
|
||||
try {
|
||||
const folder = await sqlFindFolderByPathQuery(
|
||||
tx || db,
|
||||
projectId,
|
||||
environment,
|
||||
removeTrailingSlash(path)
|
||||
)
|
||||
const folder = await sqlFindFolderByPathQuery(tx || db, projectId, environment, removeTrailingSlash(path))
|
||||
.orderBy("depth", "desc")
|
||||
.first();
|
||||
if (folder && folder.path !== removeTrailingSlash(path)) {
|
||||
@@ -263,19 +224,9 @@ export const secretFolderDALFactory = (db: TDbClient) => {
|
||||
// used in folder creation
|
||||
// even if its the original given /path1/path2
|
||||
// it will stop automatically at /path2
|
||||
const findClosestFolder = async (
|
||||
projectId: string,
|
||||
environment: string,
|
||||
path: string,
|
||||
tx?: Knex
|
||||
) => {
|
||||
const findClosestFolder = async (projectId: string, environment: string, path: string, tx?: Knex) => {
|
||||
try {
|
||||
const folder = await sqlFindFolderByPathQuery(
|
||||
tx || db,
|
||||
projectId,
|
||||
environment,
|
||||
removeTrailingSlash(path)
|
||||
)
|
||||
const folder = await sqlFindFolderByPathQuery(tx || db, projectId, environment, removeTrailingSlash(path))
|
||||
.orderBy("depth", "desc")
|
||||
.first();
|
||||
if (!folder) return;
|
||||
@@ -286,10 +237,7 @@ export const secretFolderDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const findByManySecretPath = async (
|
||||
query: Array<{ envId: string; secretPath: string }>,
|
||||
tx?: Knex
|
||||
) => {
|
||||
const findByManySecretPath = async (query: Array<{ envId: string; secretPath: string }>, tx?: Knex) => {
|
||||
try {
|
||||
const formatedQuery = query.map(({ secretPath, envId }) => ({
|
||||
envId,
|
||||
@@ -297,10 +245,7 @@ export const secretFolderDALFactory = (db: TDbClient) => {
|
||||
}));
|
||||
const folders = await sqlFindMultipleFolderByEnvPathQuery(tx || db, formatedQuery);
|
||||
return formatedQuery.map(({ envId, secretPath }) =>
|
||||
folders.find(
|
||||
({ path: targetPath, envId: targetEnvId }) =>
|
||||
targetPath === secretPath && targetEnvId === envId
|
||||
)
|
||||
folders.find(({ path: targetPath, envId: targetEnvId }) => targetPath === secretPath && targetEnvId === envId)
|
||||
);
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "FindByManySecretPath" });
|
||||
@@ -339,11 +284,7 @@ export const secretFolderDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const folder = await (tx || db)(TableName.SecretFolder)
|
||||
.where({ [`${TableName.SecretFolder}.id` as "id"]: id })
|
||||
.join(
|
||||
TableName.Environment,
|
||||
`${TableName.SecretFolder}.envId`,
|
||||
`${TableName.Environment}.id`
|
||||
)
|
||||
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.select(
|
||||
db.ref("id").withSchema(TableName.Environment).as("envId"),
|
||||
|
||||
@@ -4,21 +4,13 @@ import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { TSecretFoldersInsert } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
|
||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||
import { TSecretFolderDALFactory } from "./secret-folder-dal";
|
||||
import {
|
||||
TCreateFolderDTO,
|
||||
TDeleteFolderDTO,
|
||||
TGetFolderDTO,
|
||||
TUpdateFolderDTO
|
||||
} from "./secret-folder-types";
|
||||
import { TCreateFolderDTO, TDeleteFolderDTO, TGetFolderDTO, TUpdateFolderDTO } from "./secret-folder-types";
|
||||
import { TSecretFolderVersionDALFactory } from "./secret-folder-version-dal";
|
||||
|
||||
type TSecretFolderServiceFactoryDep = {
|
||||
@@ -38,14 +30,7 @@ export const secretFolderServiceFactory = ({
|
||||
projectEnvDAL,
|
||||
folderVersionDAL
|
||||
}: TSecretFolderServiceFactoryDep) => {
|
||||
const createFolder = async ({
|
||||
projectId,
|
||||
actor,
|
||||
actorId,
|
||||
name,
|
||||
environment,
|
||||
path: secretPath
|
||||
}: TCreateFolderDTO) => {
|
||||
const createFolder = async ({ projectId, actor, actorId, name, environment, path: secretPath }: TCreateFolderDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
@@ -53,8 +38,7 @@ export const secretFolderServiceFactory = ({
|
||||
);
|
||||
|
||||
const env = await projectEnvDAL.findOne({ projectId, slug: environment });
|
||||
if (!env)
|
||||
throw new BadRequestError({ message: "Environment not found", name: "Create folder" });
|
||||
if (!env) throw new BadRequestError({ message: "Environment not found", name: "Create folder" });
|
||||
|
||||
const folder = await folderDAL.transaction(async (tx) => {
|
||||
// the logic is simple we need to avoid creating same folder in same path multiple times
|
||||
@@ -62,12 +46,7 @@ export const secretFolderServiceFactory = ({
|
||||
// so we do a tricky move. we try to find the to be created folder path if that is exactly match return that
|
||||
// else we get some path before that then we will start creating remaining folder
|
||||
const pathWithFolder = path.join(secretPath, name);
|
||||
const parentFolder = await folderDAL.findClosestFolder(
|
||||
projectId,
|
||||
environment,
|
||||
pathWithFolder,
|
||||
tx
|
||||
);
|
||||
const parentFolder = await folderDAL.findClosestFolder(projectId, environment, pathWithFolder, tx);
|
||||
// no folder found is not possible root should be their
|
||||
if (!parentFolder) throw new BadRequestError({ message: "Secret path not found" });
|
||||
// exact folder
|
||||
@@ -78,24 +57,19 @@ export const secretFolderServiceFactory = ({
|
||||
// this is upsert folder in a path
|
||||
// we are not taking snapshots of this because
|
||||
// snapshot will be removed from automatic for all commits to user click or cron based
|
||||
const missingSegment = secretPath
|
||||
.substring(parentFolder.path.length)
|
||||
.split("/")
|
||||
.filter(Boolean);
|
||||
const missingSegment = secretPath.substring(parentFolder.path.length).split("/").filter(Boolean);
|
||||
if (missingSegment.length) {
|
||||
const newFolders: Array<TSecretFoldersInsert & { id: string }> = missingSegment.map(
|
||||
(segment) => {
|
||||
const newFolder = {
|
||||
name: segment,
|
||||
parentId: parentFolderId,
|
||||
id: uuidv4(),
|
||||
envId: env.id,
|
||||
version: 1
|
||||
};
|
||||
parentFolderId = newFolder.id;
|
||||
return newFolder;
|
||||
}
|
||||
);
|
||||
const newFolders: Array<TSecretFoldersInsert & { id: string }> = missingSegment.map((segment) => {
|
||||
const newFolder = {
|
||||
name: segment,
|
||||
parentId: parentFolderId,
|
||||
id: uuidv4(),
|
||||
envId: env.id,
|
||||
version: 1
|
||||
};
|
||||
parentFolderId = newFolder.id;
|
||||
return newFolder;
|
||||
});
|
||||
parentFolderId = newFolders.at(-1)?.id as string;
|
||||
const docs = await folderDAL.insertMany(newFolders, tx);
|
||||
await folderVersionDAL.insertMany(
|
||||
@@ -110,10 +84,7 @@ export const secretFolderServiceFactory = ({
|
||||
}
|
||||
}
|
||||
|
||||
const doc = await folderDAL.create(
|
||||
{ name, envId: env.id, version: 1, parentId: parentFolderId },
|
||||
tx
|
||||
);
|
||||
const doc = await folderDAL.create({ name, envId: env.id, version: 1, parentId: parentFolderId }, tx);
|
||||
await folderVersionDAL.create(
|
||||
{
|
||||
name: doc.name,
|
||||
@@ -149,8 +120,7 @@ export const secretFolderServiceFactory = ({
|
||||
if (!parentFolder) throw new BadRequestError({ message: "Secret path not found" });
|
||||
|
||||
const env = await projectEnvDAL.findOne({ projectId, slug: environment });
|
||||
if (!env)
|
||||
throw new BadRequestError({ message: "Environment not found", name: "Update folder" });
|
||||
if (!env) throw new BadRequestError({ message: "Environment not found", name: "Update folder" });
|
||||
const folder = await folderDAL
|
||||
.findOne({ envId: env.id, id, parentId: parentFolder.id })
|
||||
// now folder api accepts id based change
|
||||
@@ -160,11 +130,7 @@ export const secretFolderServiceFactory = ({
|
||||
if (!folder) throw new BadRequestError({ message: "Folder not found" });
|
||||
|
||||
const newFolder = await folderDAL.transaction(async (tx) => {
|
||||
const [doc] = await folderDAL.update(
|
||||
{ envId: env.id, id: folder.id, parentId: parentFolder.id },
|
||||
{ name },
|
||||
tx
|
||||
);
|
||||
const [doc] = await folderDAL.update({ envId: env.id, id: folder.id, parentId: parentFolder.id }, { name }, tx);
|
||||
await folderVersionDAL.create(
|
||||
{
|
||||
name: doc.name,
|
||||
@@ -182,14 +148,7 @@ export const secretFolderServiceFactory = ({
|
||||
return { folder: newFolder, old: folder };
|
||||
};
|
||||
|
||||
const deleteFolder = async ({
|
||||
projectId,
|
||||
actor,
|
||||
actorId,
|
||||
environment,
|
||||
path: secretPath,
|
||||
id
|
||||
}: TDeleteFolderDTO) => {
|
||||
const deleteFolder = async ({ projectId, actor, actorId, environment, path: secretPath, id }: TDeleteFolderDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
@@ -197,8 +156,7 @@ export const secretFolderServiceFactory = ({
|
||||
);
|
||||
|
||||
const env = await projectEnvDAL.findOne({ projectId, slug: environment });
|
||||
if (!env)
|
||||
throw new BadRequestError({ message: "Environment not found", name: "Create folder" });
|
||||
if (!env) throw new BadRequestError({ message: "Environment not found", name: "Create folder" });
|
||||
|
||||
const folder = await folderDAL.transaction(async (tx) => {
|
||||
const parentFolder = await folderDAL.findBySecretPath(projectId, environment, secretPath, tx);
|
||||
@@ -213,13 +171,7 @@ export const secretFolderServiceFactory = ({
|
||||
return folder;
|
||||
};
|
||||
|
||||
const getFolders = async ({
|
||||
projectId,
|
||||
actor,
|
||||
actorId,
|
||||
environment,
|
||||
path: secretPath
|
||||
}: TGetFolderDTO) => {
|
||||
const getFolders = async ({ projectId, actor, actorId, environment, path: secretPath }: TGetFolderDTO) => {
|
||||
// folder list is allowed to be read by anyone
|
||||
// permission to check does user has access
|
||||
await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
|
||||
@@ -14,11 +14,7 @@ export const secretFolderVersionDALFactory = (db: TDbClient) => {
|
||||
const findLatestVersionByFolderId = async (folderId: string, tx?: Knex) => {
|
||||
try {
|
||||
const docs = await (tx || db)(TableName.SecretFolderVersion)
|
||||
.join(
|
||||
TableName.SecretFolder,
|
||||
`${TableName.SecretFolderVersion}.folderId`,
|
||||
`${TableName.SecretFolder}.id`
|
||||
)
|
||||
.join(TableName.SecretFolder, `${TableName.SecretFolderVersion}.folderId`, `${TableName.SecretFolder}.id`)
|
||||
.where({ parentId: folderId })
|
||||
.join<TSecretFolderVersions>(
|
||||
(tx || db)(TableName.SecretFolderVersion)
|
||||
@@ -42,9 +38,7 @@ export const secretFolderVersionDALFactory = (db: TDbClient) => {
|
||||
|
||||
const findLatestFolderVersions = async (folderIds: string[], tx?: Knex) => {
|
||||
try {
|
||||
const docs: Array<TSecretFolderVersions & { max: number }> = await (tx || db)(
|
||||
TableName.SecretFolderVersion
|
||||
)
|
||||
const docs: Array<TSecretFolderVersions & { max: number }> = await (tx || db)(TableName.SecretFolderVersion)
|
||||
.whereIn("folderId", folderIds)
|
||||
.join(
|
||||
(tx || db)(TableName.SecretFolderVersion)
|
||||
|
||||
@@ -53,11 +53,7 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const docs = await (tx || db)(TableName.SecretImport)
|
||||
.where(filter)
|
||||
.join(
|
||||
TableName.Environment,
|
||||
`${TableName.SecretImport}.importEnv`,
|
||||
`${TableName.Environment}.id`
|
||||
)
|
||||
.join(TableName.Environment, `${TableName.SecretImport}.importEnv`, `${TableName.Environment}.id`)
|
||||
.select(
|
||||
db.ref("*").withSchema(TableName.SecretImport) as unknown as keyof TSecretImports,
|
||||
db.ref("slug").withSchema(TableName.Environment),
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
|
||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||
@@ -39,14 +36,7 @@ export const secretImportServiceFactory = ({
|
||||
folderDAL,
|
||||
secretDAL
|
||||
}: TSecretImportServiceFactoryDep) => {
|
||||
const createImport = async ({
|
||||
environment,
|
||||
data,
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
path
|
||||
}: TCreateSecretImportDTO) => {
|
||||
const createImport = async ({ environment, data, actor, actorId, projectId, path }: TCreateSecretImportDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
|
||||
// check if user has permission to import into destination path
|
||||
@@ -69,8 +59,7 @@ export const secretImportServiceFactory = ({
|
||||
|
||||
// TODO(akhilmhdh-pg): updated permission check add here
|
||||
const [importEnv] = await projectEnvDAL.findBySlugs(projectId, [data.environment]);
|
||||
if (!importEnv)
|
||||
throw new BadRequestError({ error: "Imported env not found", name: "Create import" });
|
||||
if (!importEnv) throw new BadRequestError({ error: "Imported env not found", name: "Create import" });
|
||||
|
||||
const secImport = await secretImportDAL.transaction(async (tx) => {
|
||||
const lastPos = await secretImportDAL.findLastImportPosition(folder.id, tx);
|
||||
@@ -88,15 +77,7 @@ export const secretImportServiceFactory = ({
|
||||
return { ...secImport, importEnv };
|
||||
};
|
||||
|
||||
const updateImport = async ({
|
||||
path,
|
||||
environment,
|
||||
projectId,
|
||||
actor,
|
||||
actorId,
|
||||
data,
|
||||
id
|
||||
}: TUpdateSecretImportDTO) => {
|
||||
const updateImport = async ({ path, environment, projectId, actor, actorId, data, id }: TUpdateSecretImportDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
@@ -112,8 +93,7 @@ export const secretImportServiceFactory = ({
|
||||
const importedEnv = data.environment // this is get env information of new one or old one
|
||||
? (await projectEnvDAL.findBySlugs(projectId, [data.environment]))?.[0]
|
||||
: await projectEnvDAL.findById(secImpDoc.importEnv);
|
||||
if (!importedEnv)
|
||||
throw new BadRequestError({ error: "Imported env not found", name: "Create import" });
|
||||
if (!importedEnv) throw new BadRequestError({ error: "Imported env not found", name: "Create import" });
|
||||
|
||||
const updatedSecImport = await secretImportDAL.transaction(async (tx) => {
|
||||
const secImp = await secretImportDAL.findOne({ folderId: folder.id, id });
|
||||
@@ -135,14 +115,7 @@ export const secretImportServiceFactory = ({
|
||||
return { ...updatedSecImport, importEnv: importedEnv };
|
||||
};
|
||||
|
||||
const deleteImport = async ({
|
||||
path,
|
||||
environment,
|
||||
projectId,
|
||||
actor,
|
||||
actorId,
|
||||
id
|
||||
}: TDeleteSecretImportDTO) => {
|
||||
const deleteImport = async ({ path, environment, projectId, actor, actorId, id }: TDeleteSecretImportDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
@@ -154,25 +127,17 @@ export const secretImportServiceFactory = ({
|
||||
|
||||
const secImport = await secretImportDAL.transaction(async (tx) => {
|
||||
const [doc] = await secretImportDAL.delete({ folderId: folder.id, id }, tx);
|
||||
if (!doc)
|
||||
throw new BadRequestError({ name: "Sec imp del", message: "Secret import doc not found" });
|
||||
if (!doc) throw new BadRequestError({ name: "Sec imp del", message: "Secret import doc not found" });
|
||||
await secretImportDAL.updateAllPosition(folder.id, doc.position, -1, tx);
|
||||
|
||||
const importEnv = await projectEnvDAL.findById(doc.importEnv);
|
||||
if (!importEnv)
|
||||
throw new BadRequestError({ error: "Imported env not found", name: "Create import" });
|
||||
if (!importEnv) throw new BadRequestError({ error: "Imported env not found", name: "Create import" });
|
||||
return { ...doc, importEnv };
|
||||
});
|
||||
return secImport;
|
||||
};
|
||||
|
||||
const getImports = async ({
|
||||
path,
|
||||
environment,
|
||||
projectId,
|
||||
actor,
|
||||
actorId
|
||||
}: TGetSecretImportsDTO) => {
|
||||
const getImports = async ({ path, environment, projectId, actor, actorId }: TGetSecretImportsDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
@@ -186,13 +151,7 @@ export const secretImportServiceFactory = ({
|
||||
return secImports;
|
||||
};
|
||||
|
||||
const getSecretsFromImports = async ({
|
||||
path,
|
||||
environment,
|
||||
projectId,
|
||||
actor,
|
||||
actorId
|
||||
}: TGetSecretsFromImportDTO) => {
|
||||
const getSecretsFromImports = async ({ path, environment, projectId, actor, actorId }: TGetSecretsFromImportDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
|
||||
@@ -23,11 +23,7 @@ export const secretTagDALFactory = (db: TDbClient) => {
|
||||
const deleteTagsManySecret = async (projectId: string, secretIds: string[], tx?: Knex) => {
|
||||
try {
|
||||
const tags = await (tx || db)(TableName.JnSecretTag)
|
||||
.join(
|
||||
TableName.SecretTag,
|
||||
`${TableName.JnSecretTag}.${TableName.SecretTag}Id`,
|
||||
`${TableName.SecretTag}.id`
|
||||
)
|
||||
.join(TableName.SecretTag, `${TableName.JnSecretTag}.${TableName.SecretTag}Id`, `${TableName.SecretTag}.id`)
|
||||
.where("projectId", projectId)
|
||||
.whereIn(`${TableName.Secret}Id`, secretIds)
|
||||
.delete()
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
|
||||
import { TSecretTagDALFactory } from "./secret-tag-dal";
|
||||
@@ -17,16 +14,10 @@ type TSecretTagServiceFactoryDep = {
|
||||
|
||||
export type TSecretTagServiceFactory = ReturnType<typeof secretTagServiceFactory>;
|
||||
|
||||
export const secretTagServiceFactory = ({
|
||||
secretTagDAL,
|
||||
permissionService
|
||||
}: TSecretTagServiceFactoryDep) => {
|
||||
export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSecretTagServiceFactoryDep) => {
|
||||
const createTag = async ({ name, slug, actor, color, actorId, projectId }: TCreateTagDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Tags
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Tags);
|
||||
|
||||
const existingTag = await secretTagDAL.findOne({ slug });
|
||||
if (existingTag) throw new BadRequestError({ message: "Tag already exist" });
|
||||
@@ -45,15 +36,8 @@ export const secretTagServiceFactory = ({
|
||||
const tag = await secretTagDAL.findById(id);
|
||||
if (!tag) throw new BadRequestError({ message: "Tag doesn't exist" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
tag.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.Tags
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, tag.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Tags);
|
||||
|
||||
const deletedTag = await secretTagDAL.deleteById(tag.id);
|
||||
return deletedTag;
|
||||
@@ -61,10 +45,7 @@ export const secretTagServiceFactory = ({
|
||||
|
||||
const getProjectTags = async ({ actor, actorId, projectId }: TListProjectTagsDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Tags
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
|
||||
|
||||
const tags = await secretTagDAL.find({ projectId });
|
||||
return tags;
|
||||
|
||||
@@ -11,17 +11,9 @@ export type TSecretDALFactory = ReturnType<typeof secretDALFactory>;
|
||||
export const secretDALFactory = (db: TDbClient) => {
|
||||
const secretOrm = ormify(db, TableName.Secret);
|
||||
|
||||
const update = async (
|
||||
filter: Partial<TSecrets>,
|
||||
data: Omit<TSecretsUpdate, "version">,
|
||||
tx?: Knex
|
||||
) => {
|
||||
const update = async (filter: Partial<TSecrets>, data: Omit<TSecretsUpdate, "version">, tx?: Knex) => {
|
||||
try {
|
||||
const sec = await (tx || db)(TableName.Secret)
|
||||
.where(filter)
|
||||
.update(data)
|
||||
.increment("version", 1)
|
||||
.returning("*");
|
||||
const sec = await (tx || db)(TableName.Secret).where(filter).update(data).increment("version", 1).returning("*");
|
||||
return sec;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "update secret" });
|
||||
@@ -30,10 +22,7 @@ export const secretDALFactory = (db: TDbClient) => {
|
||||
|
||||
// the idea is to use postgres specific function
|
||||
// insert with id this will cause a conflict then merge the data
|
||||
const bulkUpdate = async (
|
||||
data: Array<{ filter: Partial<TSecrets>; data: TSecretsUpdate }>,
|
||||
tx?: Knex
|
||||
) => {
|
||||
const bulkUpdate = async (data: Array<{ filter: Partial<TSecrets>; data: TSecretsUpdate }>, tx?: Knex) => {
|
||||
try {
|
||||
const secs = await Promise.all(
|
||||
data.map(async ({ filter, data: updateData }) => {
|
||||
@@ -91,16 +80,8 @@ export const secretDALFactory = (db: TDbClient) => {
|
||||
.where((bd) => {
|
||||
void bd.whereNull("userId").orWhere({ userId: userId || null });
|
||||
})
|
||||
.leftJoin(
|
||||
TableName.JnSecretTag,
|
||||
`${TableName.Secret}.id`,
|
||||
`${TableName.JnSecretTag}.${TableName.Secret}Id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.SecretTag,
|
||||
`${TableName.JnSecretTag}.${TableName.SecretTag}Id`,
|
||||
`${TableName.SecretTag}.id`
|
||||
)
|
||||
.leftJoin(TableName.JnSecretTag, `${TableName.Secret}.id`, `${TableName.JnSecretTag}.${TableName.Secret}Id`)
|
||||
.leftJoin(TableName.SecretTag, `${TableName.JnSecretTag}.${TableName.SecretTag}Id`, `${TableName.SecretTag}.id`)
|
||||
.select(selectAllTableCols(TableName.Secret))
|
||||
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
||||
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
||||
|
||||
@@ -8,10 +8,7 @@ import { buildSecretBlindIndexFromName, decryptSymmetric128BitHexKeyUTF8 } from
|
||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||
import { TSecretDALFactory } from "./secret-dal";
|
||||
|
||||
export const generateSecretBlindIndexBySalt = async (
|
||||
secretName: string,
|
||||
secretBlindIndexDoc: TSecretBlindIndexes
|
||||
) => {
|
||||
export const generateSecretBlindIndexBySalt = async (secretName: string, secretBlindIndexDoc: TSecretBlindIndexes) => {
|
||||
const appCfg = getConfig();
|
||||
const secretBlindIndex = await buildSecretBlindIndexFromName({
|
||||
secretName,
|
||||
@@ -32,12 +29,7 @@ type TInterpolateSecretArg = {
|
||||
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">;
|
||||
};
|
||||
|
||||
export const interpolateSecrets = ({
|
||||
projectId,
|
||||
secretEncKey,
|
||||
secretDAL,
|
||||
folderDAL
|
||||
}: TInterpolateSecretArg) => {
|
||||
export const interpolateSecrets = ({ projectId, secretEncKey, secretDAL, folderDAL }: TInterpolateSecretArg) => {
|
||||
const fetchSecretsCrossEnv = () => {
|
||||
const fetchCache: Record<string, Record<string, string>> = {};
|
||||
|
||||
@@ -197,10 +189,7 @@ export const interpolateSecrets = ({
|
||||
return expandSecrets;
|
||||
};
|
||||
|
||||
export const decryptSecretRaw = (
|
||||
secret: TSecrets & { workspace: string; environment: string },
|
||||
key: string
|
||||
) => {
|
||||
export const decryptSecretRaw = (secret: TSecrets & { workspace: string; environment: string }, key: string) => {
|
||||
const secretKey = decryptSymmetric128BitHexKeyUTF8({
|
||||
ciphertext: secret.secretKeyCiphertext,
|
||||
iv: secret.secretKeyIV,
|
||||
|
||||
@@ -23,11 +23,7 @@ import { TWebhookDALFactory } from "../webhook/webhook-dal";
|
||||
import { fnTriggerWebhook } from "../webhook/webhook-fns";
|
||||
import { TSecretDALFactory } from "./secret-dal";
|
||||
import { interpolateSecrets } from "./secret-fns";
|
||||
import {
|
||||
TCreateSecretReminderDTO,
|
||||
THandleReminderDTO,
|
||||
TRemoveSecretReminderDTO
|
||||
} from "./secret-types";
|
||||
import { TCreateSecretReminderDTO, THandleReminderDTO, TRemoveSecretReminderDTO } from "./secret-types";
|
||||
|
||||
export type TSecretQueueFactory = ReturnType<typeof secretQueueFactory>;
|
||||
|
||||
@@ -105,20 +101,13 @@ export const secretQueueFactory = ({
|
||||
QueueJobs.SecretReminder,
|
||||
{
|
||||
// on prod it this will be in days, in development this will be second
|
||||
every:
|
||||
appCfg.NODE_ENV === "development"
|
||||
? secondsToMillis(dto.repeatDays)
|
||||
: daysToMillisecond(dto.repeatDays)
|
||||
every: appCfg.NODE_ENV === "development" ? secondsToMillis(dto.repeatDays) : daysToMillisecond(dto.repeatDays)
|
||||
},
|
||||
`reminder-${dto.secretId}`
|
||||
);
|
||||
};
|
||||
|
||||
const addSecretReminder = async ({
|
||||
oldSecret,
|
||||
newSecret,
|
||||
projectId
|
||||
}: TCreateSecretReminderDTO) => {
|
||||
const addSecretReminder = async ({ oldSecret, newSecret, projectId }: TCreateSecretReminderDTO) => {
|
||||
try {
|
||||
const appCfg = getConfig();
|
||||
|
||||
@@ -179,8 +168,7 @@ export const secretQueueFactory = ({
|
||||
|
||||
if (newSecret.type !== "personal" && secretReminderRepeatDays !== undefined) {
|
||||
if (
|
||||
(secretReminderRepeatDays &&
|
||||
oldSecret.secretReminderRepeatDays !== secretReminderRepeatDays) ||
|
||||
(secretReminderRepeatDays && oldSecret.secretReminderRepeatDays !== secretReminderRepeatDays) ||
|
||||
(secretReminderNote && oldSecret.secretReminderNote !== secretReminderNote)
|
||||
) {
|
||||
await addSecretReminder({
|
||||
@@ -212,10 +200,7 @@ export const secretQueueFactory = ({
|
||||
secretDAL,
|
||||
folderDAL
|
||||
});
|
||||
const content: Record<
|
||||
string,
|
||||
{ value: string; comment?: string; skipMultilineEncoding?: boolean }
|
||||
> = {};
|
||||
const content: Record<string, { value: string; comment?: string; skipMultilineEncoding?: boolean }> = {};
|
||||
|
||||
importedSecrets.forEach(({ secrets: secs }) => {
|
||||
secs.forEach((secret) => {
|
||||
@@ -294,8 +279,7 @@ export const secretQueueFactory = ({
|
||||
|
||||
const integrations = await integrationDAL.findByProjectIdV2(projectId, environment);
|
||||
const toBeSyncedIntegrations = integrations.filter(
|
||||
({ secretPath: integrationSecPath, isActive }) =>
|
||||
isActive && isSamePath(secretPath, integrationSecPath)
|
||||
({ secretPath: integrationSecPath, isActive }) => isActive && isSamePath(secretPath, integrationSecPath)
|
||||
);
|
||||
|
||||
if (!integrations.length) return;
|
||||
@@ -309,14 +293,8 @@ export const secretQueueFactory = ({
|
||||
};
|
||||
|
||||
const botKey = await projectBotService.getBotKey(projectId);
|
||||
const { accessToken, accessId } = await integrationAuthService.getIntegrationAccessToken(
|
||||
integrationAuth,
|
||||
botKey
|
||||
);
|
||||
const secrets = await getIntegrationSecrets(
|
||||
{ environment, projectId, secretPath, folderId: folder.id },
|
||||
botKey
|
||||
);
|
||||
const { accessToken, accessId } = await integrationAuthService.getIntegrationAccessToken(integrationAuth, botKey);
|
||||
const secrets = await getIntegrationSecrets({ environment, projectId, secretPath, folderId: folder.id }, botKey);
|
||||
const suffixedSecrets: typeof secrets = {};
|
||||
const metadata = integration.metadata as Record<string, string>;
|
||||
if (metadata) {
|
||||
@@ -353,25 +331,19 @@ export const secretQueueFactory = ({
|
||||
const project = await projectDAL.findById(projectId);
|
||||
|
||||
if (!organization) {
|
||||
logger.info(
|
||||
`secretReminderQueue.process: [secretDocument=${data.secretId}] no organization found`
|
||||
);
|
||||
logger.info(`secretReminderQueue.process: [secretDocument=${data.secretId}] no organization found`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!project) {
|
||||
logger.info(
|
||||
`secretReminderQueue.process: [secretDocument=${data.secretId}] no project found`
|
||||
);
|
||||
logger.info(`secretReminderQueue.process: [secretDocument=${data.secretId}] no project found`);
|
||||
return;
|
||||
}
|
||||
|
||||
const projectMembers = await projectMembershipDAL.findAllProjectMembers(projectId);
|
||||
|
||||
if (!projectMembers || !projectMembers.length) {
|
||||
logger.info(
|
||||
`secretReminderQueue.process: [secretDocument=${data.secretId}] no project members found`
|
||||
);
|
||||
logger.info(`secretReminderQueue.process: [secretDocument=${data.secretId}] no project members found`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
|
||||
import {
|
||||
SecretEncryptionAlgo,
|
||||
SecretKeyEncoding,
|
||||
SecretsSchema,
|
||||
SecretType,
|
||||
TableName
|
||||
} from "@app/db/schemas";
|
||||
import { SecretEncryptionAlgo, SecretKeyEncoding, SecretsSchema, SecretType, TableName } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { buildSecretBlindIndexFromName, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
||||
@@ -58,17 +49,11 @@ type TSecretServiceFactoryDep = {
|
||||
secretDAL: TSecretDALFactory;
|
||||
secretTagDAL: TSecretTagDALFactory;
|
||||
secretVersionDAL: TSecretVersionDALFactory;
|
||||
folderDAL: Pick<
|
||||
TSecretFolderDALFactory,
|
||||
"findBySecretPath" | "updateById" | "findById" | "findByManySecretPath"
|
||||
>;
|
||||
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath" | "updateById" | "findById" | "findByManySecretPath">;
|
||||
secretBlindIndexDAL: TSecretBlindIndexDALFactory;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
|
||||
secretQueueService: Pick<
|
||||
TSecretQueueFactory,
|
||||
"syncSecrets" | "handleSecretReminder" | "removeSecretReminder"
|
||||
>;
|
||||
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "handleSecretReminder" | "removeSecretReminder">;
|
||||
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
|
||||
secretImportDAL: Pick<TSecretImportDALFactory, "find">;
|
||||
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
|
||||
@@ -93,8 +78,7 @@ export const secretServiceFactory = ({
|
||||
const appCfg = getConfig();
|
||||
|
||||
const secretBlindIndexDoc = await secretBlindIndexDAL.findOne({ projectId });
|
||||
if (!secretBlindIndexDoc)
|
||||
throw new BadRequestError({ message: "Blind index not found", name: "Create secret" });
|
||||
if (!secretBlindIndexDoc) throw new BadRequestError({ message: "Blind index not found", name: "Create secret" });
|
||||
|
||||
const secretBlindIndex = await buildSecretBlindIndexFromName({
|
||||
secretName,
|
||||
@@ -116,15 +100,11 @@ export const secretServiceFactory = ({
|
||||
inputSecrets.map(({ tags, ...el }) => ({ ...el, folderId })),
|
||||
tx
|
||||
);
|
||||
const newSecretGroupByBlindIndex = groupBy(
|
||||
newSecrets,
|
||||
(item) => item.secretBlindIndex as string
|
||||
);
|
||||
const newSecretGroupByBlindIndex = groupBy(newSecrets, (item) => item.secretBlindIndex as string);
|
||||
const newSecretTags = inputSecrets.flatMap(({ tags: secretTags = [], secretBlindIndex }) =>
|
||||
secretTags.map((tag) => ({
|
||||
[`${TableName.SecretTag}Id` as const]: tag,
|
||||
[`${TableName.Secret}Id` as const]:
|
||||
newSecretGroupByBlindIndex[secretBlindIndex as string][0].id
|
||||
[`${TableName.Secret}Id` as const]: newSecretGroupByBlindIndex[secretBlindIndex as string][0].id
|
||||
}))
|
||||
);
|
||||
const secretVersions = await secretVersionDAL.insertMany(
|
||||
@@ -148,12 +128,7 @@ export const secretServiceFactory = ({
|
||||
return newSecrets.map((secret) => ({ ...secret, _id: secret.id }));
|
||||
};
|
||||
|
||||
const fnSecretBulkUpdate = async ({
|
||||
tx,
|
||||
inputSecrets,
|
||||
folderId,
|
||||
projectId
|
||||
}: TFnSecretBulkUpdate) => {
|
||||
const fnSecretBulkUpdate = async ({ tx, inputSecrets, folderId, projectId }: TFnSecretBulkUpdate) => {
|
||||
const newSecrets = await secretDAL.bulkUpdate(
|
||||
inputSecrets.map(({ filter, data: { tags, ...data } }) => ({
|
||||
filter: { ...filter, folderId },
|
||||
@@ -197,12 +172,7 @@ export const secretServiceFactory = ({
|
||||
return newSecrets.map((secret) => ({ ...secret, _id: secret.id }));
|
||||
};
|
||||
|
||||
const fnSecretBulkDelete = async ({
|
||||
folderId,
|
||||
inputSecrets,
|
||||
tx,
|
||||
actorId
|
||||
}: TFnSecretBulkDelete) => {
|
||||
const fnSecretBulkDelete = async ({ folderId, inputSecrets, tx, actorId }: TFnSecretBulkDelete) => {
|
||||
const deletedSecrets = await secretDAL.deleteMany(
|
||||
inputSecrets.map(({ type, secretBlindIndex }) => ({
|
||||
blindIndex: secretBlindIndex,
|
||||
@@ -245,9 +215,7 @@ export const secretServiceFactory = ({
|
||||
}: TFnSecretBlindIndexCheck) => {
|
||||
const blindIndex2KeyName: Record<string, string> = {}; // used at audit log point
|
||||
const keyName2BlindIndex = await Promise.all(
|
||||
inputSecrets.map(({ secretName }) =>
|
||||
generateSecretBlindIndexBySalt(secretName, blindIndexCfg)
|
||||
)
|
||||
inputSecrets.map(({ secretName }) => generateSecretBlindIndexBySalt(secretName, blindIndexCfg))
|
||||
).then((blindIndexes) =>
|
||||
blindIndexes.reduce<Record<string, string>>((prev, curr, i) => {
|
||||
// eslint-disable-next-line
|
||||
@@ -282,11 +250,7 @@ export const secretServiceFactory = ({
|
||||
|
||||
// this is used when secret blind index already exist
|
||||
// mainly for secret approval
|
||||
const fnSecretBlindIndexCheckV2 = async ({
|
||||
inputSecrets,
|
||||
folderId,
|
||||
userId
|
||||
}: TFnSecretBlindIndexCheckV2) => {
|
||||
const fnSecretBlindIndexCheckV2 = async ({ inputSecrets, folderId, userId }: TFnSecretBlindIndexCheckV2) => {
|
||||
if (inputSecrets.some(({ type }) => type === SecretType.Personal) && !userId) {
|
||||
throw new BadRequestError({ message: "Missing user id for personal secret" });
|
||||
}
|
||||
@@ -303,14 +267,7 @@ export const secretServiceFactory = ({
|
||||
return { secsGroupedByBlindIndex, secrets };
|
||||
};
|
||||
|
||||
const createSecret = async ({
|
||||
path,
|
||||
actor,
|
||||
actorId,
|
||||
environment,
|
||||
projectId,
|
||||
...inputSecret
|
||||
}: TCreateSecretDTO) => {
|
||||
const createSecret = async ({ path, actor, actorId, environment, projectId, ...inputSecret }: TCreateSecretDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
@@ -322,8 +279,7 @@ export const secretServiceFactory = ({
|
||||
const folderId = folder.id;
|
||||
|
||||
const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId });
|
||||
if (!blindIndexCfg)
|
||||
throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" });
|
||||
if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" });
|
||||
|
||||
if (ActorType.USER !== actor && inputSecret.type === SecretType.Personal) {
|
||||
throw new BadRequestError({ message: "Must be user to create personal secret" });
|
||||
@@ -352,11 +308,8 @@ export const secretServiceFactory = ({
|
||||
|
||||
// validate tags
|
||||
// fetch all tags and if not same count throw error meaning one was invalid tags
|
||||
const tags = inputSecret.tags
|
||||
? await secretTagDAL.findManyTagsById(projectId, inputSecret.tags)
|
||||
: [];
|
||||
if ((inputSecret.tags || []).length !== tags.length)
|
||||
throw new BadRequestError({ message: "Tag not found" });
|
||||
const tags = inputSecret.tags ? await secretTagDAL.findManyTagsById(projectId, inputSecret.tags) : [];
|
||||
if ((inputSecret.tags || []).length !== tags.length) throw new BadRequestError({ message: "Tag not found" });
|
||||
|
||||
const { secretName, type, ...el } = inputSecret;
|
||||
const secret = await secretDAL.transaction((tx) =>
|
||||
@@ -384,14 +337,7 @@ export const secretServiceFactory = ({
|
||||
return { ...secret[0], environment, workspace: projectId, tags };
|
||||
};
|
||||
|
||||
const updateSecret = async ({
|
||||
path,
|
||||
actor,
|
||||
actorId,
|
||||
environment,
|
||||
projectId,
|
||||
...inputSecret
|
||||
}: TUpdateSecretDTO) => {
|
||||
const updateSecret = async ({ path, actor, actorId, environment, projectId, ...inputSecret }: TUpdateSecretDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
@@ -403,8 +349,7 @@ export const secretServiceFactory = ({
|
||||
const folderId = folder.id;
|
||||
|
||||
const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId });
|
||||
if (!blindIndexCfg)
|
||||
throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" });
|
||||
if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" });
|
||||
|
||||
if (ActorType.USER !== actor && inputSecret.type === SecretType.Personal) {
|
||||
throw new BadRequestError({ message: "Must be user to create personal secret" });
|
||||
@@ -441,11 +386,8 @@ export const secretServiceFactory = ({
|
||||
projectId
|
||||
});
|
||||
|
||||
const tags = inputSecret.tags
|
||||
? await secretTagDAL.findManyTagsById(projectId, inputSecret.tags)
|
||||
: [];
|
||||
if ((inputSecret.tags || []).length !== tags.length)
|
||||
throw new BadRequestError({ message: "Tag not found" });
|
||||
const tags = inputSecret.tags ? await secretTagDAL.findManyTagsById(projectId, inputSecret.tags) : [];
|
||||
if ((inputSecret.tags || []).length !== tags.length) throw new BadRequestError({ message: "Tag not found" });
|
||||
|
||||
const { secretName, ...el } = inputSecret;
|
||||
const updatedSecret = await secretDAL.transaction(async (tx) =>
|
||||
@@ -487,14 +429,7 @@ export const secretServiceFactory = ({
|
||||
return { ...updatedSecret[0], workspace: projectId, environment };
|
||||
};
|
||||
|
||||
const deleteSecret = async ({
|
||||
path,
|
||||
actor,
|
||||
actorId,
|
||||
environment,
|
||||
projectId,
|
||||
...inputSecret
|
||||
}: TDeleteSecretDTO) => {
|
||||
const deleteSecret = async ({ path, actor, actorId, environment, projectId, ...inputSecret }: TDeleteSecretDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
@@ -506,8 +441,7 @@ export const secretServiceFactory = ({
|
||||
const folderId = folder.id;
|
||||
|
||||
const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId });
|
||||
if (!blindIndexCfg)
|
||||
throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" });
|
||||
if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" });
|
||||
|
||||
if (ActorType.USER !== actor && inputSecret.type === SecretType.Personal) {
|
||||
throw new BadRequestError({ message: "Must be user to create personal secret" });
|
||||
@@ -542,14 +476,7 @@ export const secretServiceFactory = ({
|
||||
return { ...deletedSecret[0], _id: deletedSecret[0].id, workspace: projectId, environment };
|
||||
};
|
||||
|
||||
const getSecrets = async ({
|
||||
actorId,
|
||||
path,
|
||||
environment,
|
||||
projectId,
|
||||
actor,
|
||||
includeImports
|
||||
}: TGetSecretsDTO) => {
|
||||
const getSecrets = async ({ actorId, path, environment, projectId, actor, includeImports }: TGetSecretsDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
@@ -683,8 +610,7 @@ export const secretServiceFactory = ({
|
||||
const folderId = folder.id;
|
||||
|
||||
const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId });
|
||||
if (!blindIndexCfg)
|
||||
throw new BadRequestError({ message: "Blind index not found", name: "Update secret" });
|
||||
if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" });
|
||||
|
||||
const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
||||
inputSecrets,
|
||||
@@ -738,8 +664,7 @@ export const secretServiceFactory = ({
|
||||
const folderId = folder.id;
|
||||
|
||||
const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId });
|
||||
if (!blindIndexCfg)
|
||||
throw new BadRequestError({ message: "Blind index not found", name: "Update secret" });
|
||||
if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" });
|
||||
|
||||
const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
||||
inputSecrets,
|
||||
@@ -809,8 +734,7 @@ export const secretServiceFactory = ({
|
||||
const folderId = folder.id;
|
||||
|
||||
const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId });
|
||||
if (!blindIndexCfg)
|
||||
throw new BadRequestError({ message: "Blind index not found", name: "Update secret" });
|
||||
if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" });
|
||||
|
||||
const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
||||
inputSecrets,
|
||||
@@ -838,46 +762,22 @@ export const secretServiceFactory = ({
|
||||
return secretsDeleted;
|
||||
};
|
||||
|
||||
const listSecretVersionsBySecretId = async ({
|
||||
actorId,
|
||||
actor,
|
||||
limit,
|
||||
offset,
|
||||
secretId
|
||||
}: TListSecretVersionDTO) => {
|
||||
const listSecretVersionsBySecretId = async ({ actorId, actor, limit, offset, secretId }: TListSecretVersionDTO) => {
|
||||
const secret = await secretDAL.findById(secretId);
|
||||
if (!secret) throw new BadRequestError({ message: "Failed to find secret" });
|
||||
|
||||
const folder = await folderDAL.findById(secret.folderId);
|
||||
if (!folder) throw new BadRequestError({ message: "Folder not found" });
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
folder.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.SecretRollback
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, folder.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||
|
||||
const secretVersions = await secretVersionDAL.find(
|
||||
{ secretId },
|
||||
{ limit, offset, sort: [["createdAt", "desc"]] }
|
||||
);
|
||||
const secretVersions = await secretVersionDAL.find({ secretId }, { limit, offset, sort: [["createdAt", "desc"]] });
|
||||
return secretVersions;
|
||||
};
|
||||
|
||||
const getSecretsRaw = async ({
|
||||
projectId,
|
||||
path,
|
||||
actor,
|
||||
actorId,
|
||||
environment,
|
||||
includeImports
|
||||
}: TGetSecretsRawDTO) => {
|
||||
const getSecretsRaw = async ({ projectId, path, actor, actorId, environment, includeImports }: TGetSecretsRawDTO) => {
|
||||
const botKey = await projectBotService.getBotKey(projectId);
|
||||
if (!botKey)
|
||||
throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
|
||||
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
|
||||
|
||||
const { secrets, imports } = await getSecrets({
|
||||
actorId,
|
||||
@@ -911,8 +811,7 @@ export const secretServiceFactory = ({
|
||||
version
|
||||
}: TGetASecretRawDTO) => {
|
||||
const botKey = await projectBotService.getBotKey(projectId);
|
||||
if (!botKey)
|
||||
throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
|
||||
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
|
||||
|
||||
const secret = await getSecretByName({
|
||||
actorId,
|
||||
@@ -941,8 +840,7 @@ export const secretServiceFactory = ({
|
||||
skipMultilineEncoding
|
||||
}: TCreateSecretRawDTO) => {
|
||||
const botKey = await projectBotService.getBotKey(projectId);
|
||||
if (!botKey)
|
||||
throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
|
||||
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
|
||||
|
||||
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secretName, botKey);
|
||||
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey);
|
||||
@@ -986,8 +884,7 @@ export const secretServiceFactory = ({
|
||||
skipMultilineEncoding
|
||||
}: TUpdateSecretRawDTO) => {
|
||||
const botKey = await projectBotService.getBotKey(projectId);
|
||||
if (!botKey)
|
||||
throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
|
||||
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
|
||||
|
||||
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey);
|
||||
|
||||
@@ -1021,8 +918,7 @@ export const secretServiceFactory = ({
|
||||
secretPath
|
||||
}: TDeleteSecretRawDTO) => {
|
||||
const botKey = await projectBotService.getBotKey(projectId);
|
||||
if (!botKey)
|
||||
throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
|
||||
if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" });
|
||||
|
||||
const secret = await deleteSecret({
|
||||
secretName,
|
||||
@@ -1040,33 +936,17 @@ export const secretServiceFactory = ({
|
||||
return decryptSecretRaw(secret, botKey);
|
||||
};
|
||||
|
||||
const getSecretVersions = async ({
|
||||
actorId,
|
||||
actor,
|
||||
limit = 20,
|
||||
offset = 0,
|
||||
secretId
|
||||
}: TGetSecretVersionsDTO) => {
|
||||
const getSecretVersions = async ({ actorId, actor, limit = 20, offset = 0, secretId }: TGetSecretVersionsDTO) => {
|
||||
const secret = await secretDAL.findById(secretId);
|
||||
if (!secret) throw new BadRequestError({ message: "Failed to find secret" });
|
||||
|
||||
const folder = await folderDAL.findById(secret.folderId);
|
||||
if (!folder) throw new BadRequestError({ message: "Failed to find secret" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
folder.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.SecretRollback
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, folder.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||
|
||||
const secretVersions = await secretVersionDAL.find(
|
||||
{ secretId },
|
||||
{ offset, limit, sort: [["createdAt", "desc"]] }
|
||||
);
|
||||
const secretVersions = await secretVersionDAL.find({ secretId }, { offset, limit, sort: [["createdAt", "desc"]] });
|
||||
return secretVersions;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import {
|
||||
SecretType,
|
||||
TSecretBlindIndexes,
|
||||
TSecrets,
|
||||
TSecretsInsert,
|
||||
TSecretsUpdate
|
||||
} from "@app/db/schemas";
|
||||
import { SecretType, TSecretBlindIndexes, TSecrets, TSecretsInsert, TSecretsUpdate } from "@app/db/schemas";
|
||||
import { TProjectPermission } from "@app/lib/types";
|
||||
|
||||
type TPartialSecret = Pick<TSecrets, "id" | "secretReminderRepeatDays" | "secretReminderNote">;
|
||||
|
||||
type TPartialInputSecret = Pick<
|
||||
TSecrets,
|
||||
"type" | "secretReminderNote" | "secretReminderRepeatDays" | "id"
|
||||
>;
|
||||
type TPartialInputSecret = Pick<TSecrets, "type" | "secretReminderNote" | "secretReminderRepeatDays" | "id">;
|
||||
|
||||
export type TCreateSecretDTO = {
|
||||
secretName: string;
|
||||
|
||||
@@ -38,17 +38,11 @@ export const secretVersionDALFactory = (db: TDbClient) => {
|
||||
|
||||
const findLatestVersionMany = async (folderId: string, secretIds: string[], tx?: Knex) => {
|
||||
try {
|
||||
const docs: Array<TSecretVersions & { max: number }> = await (tx || db)(
|
||||
TableName.SecretVersion
|
||||
)
|
||||
const docs: Array<TSecretVersions & { max: number }> = await (tx || db)(TableName.SecretVersion)
|
||||
.where("folderId", folderId)
|
||||
.whereIn(`${TableName.SecretVersion}.secretId`, secretIds)
|
||||
.join(
|
||||
(tx || db)(TableName.SecretVersion)
|
||||
.groupBy("secretId")
|
||||
.max("version")
|
||||
.select("secretId")
|
||||
.as("latestVersion"),
|
||||
(tx || db)(TableName.SecretVersion).groupBy("secretId").max("version").select("secretId").as("latestVersion"),
|
||||
(bd) => {
|
||||
bd.on(`${TableName.SecretVersion}.secretId`, "latestVersion.secretId").andOn(
|
||||
`${TableName.SecretVersion}.version`,
|
||||
|
||||
@@ -4,10 +4,7 @@ import { ForbiddenError, subject } from "@casl/ability";
|
||||
import bcrypt from "bcrypt";
|
||||
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
|
||||
@@ -50,10 +47,7 @@ export const serviceTokenServiceFactory = ({
|
||||
encryptedKey
|
||||
}: TCreateServiceTokenDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.ServiceTokens
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens);
|
||||
|
||||
scopes.forEach(({ environment, secretPath }) => {
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
@@ -67,8 +61,7 @@ export const serviceTokenServiceFactory = ({
|
||||
// validates env
|
||||
const scopeEnvs = [...new Set(scopes.map(({ environment }) => environment))];
|
||||
const inputEnvs = await projectEnvDAL.findBySlugs(projectId, scopeEnvs);
|
||||
if (inputEnvs.length !== scopeEnvs.length)
|
||||
throw new BadRequestError({ message: "Environment not found" });
|
||||
if (inputEnvs.length !== scopeEnvs.length) throw new BadRequestError({ message: "Environment not found" });
|
||||
|
||||
const secret = crypto.randomBytes(16).toString("hex");
|
||||
const secretHash = await bcrypt.hash(secret, appCfg.SALT_ROUNDS);
|
||||
@@ -101,23 +94,15 @@ export const serviceTokenServiceFactory = ({
|
||||
const serviceToken = await serviceTokenDAL.findById(id);
|
||||
if (!serviceToken) throw new BadRequestError({ message: "Token not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
serviceToken.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.ServiceTokens
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, serviceToken.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.ServiceTokens);
|
||||
|
||||
const deletedServiceToken = await serviceTokenDAL.deleteById(id);
|
||||
return deletedServiceToken;
|
||||
};
|
||||
|
||||
const getServiceToken = async ({ actor, actorId }: TGetServiceTokenInfoDTO) => {
|
||||
if (actor !== ActorType.SERVICE)
|
||||
throw new BadRequestError({ message: "Service token not found" });
|
||||
if (actor !== ActorType.SERVICE) throw new BadRequestError({ message: "Service token not found" });
|
||||
|
||||
const serviceToken = await serviceTokenDAL.findById(actorId);
|
||||
if (!serviceToken) throw new BadRequestError({ message: "Token not found" });
|
||||
@@ -128,16 +113,9 @@ export const serviceTokenServiceFactory = ({
|
||||
return { serviceToken, user: serviceTokenUser };
|
||||
};
|
||||
|
||||
const getProjectServiceTokens = async ({
|
||||
actorId,
|
||||
actor,
|
||||
projectId
|
||||
}: TProjectServiceTokensDTO) => {
|
||||
const getProjectServiceTokens = async ({ actorId, actor, projectId }: TProjectServiceTokensDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.ServiceTokens
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
|
||||
|
||||
const tokens = await serviceTokenDAL.find({ projectId });
|
||||
return tokens;
|
||||
|
||||
@@ -19,8 +19,7 @@ export type TSuperAdminServiceFactory = ReturnType<typeof superAdminServiceFacto
|
||||
|
||||
let serverCfg: Readonly<TSuperAdmin>;
|
||||
export const getServerCfg = () => {
|
||||
if (!serverCfg)
|
||||
throw new BadRequestError({ name: "Get server cfg", message: "Server cfg not initialized" });
|
||||
if (!serverCfg) throw new BadRequestError({ name: "Get server cfg", message: "Server cfg not initialized" });
|
||||
return serverCfg;
|
||||
};
|
||||
|
||||
@@ -64,8 +63,7 @@ export const superAdminServiceFactory = ({
|
||||
userAgent
|
||||
}: TAdminSignUpDTO) => {
|
||||
const existingUser = await userDAL.findOne({ email });
|
||||
if (existingUser)
|
||||
throw new BadRequestError({ name: "Admin sign up", message: "User already exist" });
|
||||
if (existingUser) throw new BadRequestError({ name: "Admin sign up", message: "User already exist" });
|
||||
|
||||
const userInfo = await userDAL.transaction(async (tx) => {
|
||||
const newUser = await userDAL.create(
|
||||
|
||||
@@ -24,11 +24,7 @@ export const userDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
return await db(TableName.Users)
|
||||
.where({ email })
|
||||
.join(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.Users}.id`,
|
||||
`${TableName.UserEncryptionKey}.userId`
|
||||
)
|
||||
.join(TableName.UserEncryptionKey, `${TableName.Users}.id`, `${TableName.UserEncryptionKey}.userId`)
|
||||
.first();
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find user enc by email" });
|
||||
@@ -39,11 +35,7 @@ export const userDALFactory = (db: TDbClient) => {
|
||||
try {
|
||||
const user = await db(TableName.Users)
|
||||
.where(`${TableName.Users}.id`, userId)
|
||||
.join(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.Users}.id`,
|
||||
`${TableName.UserEncryptionKey}.userId`
|
||||
)
|
||||
.join(TableName.UserEncryptionKey, `${TableName.Users}.id`, `${TableName.UserEncryptionKey}.userId`)
|
||||
.first();
|
||||
if (user?.id) {
|
||||
// change to user id
|
||||
@@ -64,11 +56,7 @@ export const userDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const updateUserEncryptionByUserId = async (
|
||||
userId: string,
|
||||
data: TUserEncryptionKeysUpdate,
|
||||
tx?: Knex
|
||||
) => {
|
||||
const updateUserEncryptionByUserId = async (userId: string, data: TUserEncryptionKeysUpdate, tx?: Knex) => {
|
||||
try {
|
||||
const [userEnc] = await (tx || db)(TableName.UserEncryptionKey)
|
||||
.where({ userId })
|
||||
@@ -86,10 +74,7 @@ export const userDALFactory = (db: TDbClient) => {
|
||||
tx?: Knex
|
||||
) => {
|
||||
try {
|
||||
const [userEnc] = await (tx
|
||||
? tx(TableName.UserEncryptionKey)
|
||||
: db(TableName.UserEncryptionKey)
|
||||
)
|
||||
const [userEnc] = await (tx ? tx(TableName.UserEncryptionKey) : db(TableName.UserEncryptionKey))
|
||||
// if user insert make sure to pass all required data
|
||||
.insert({ userId, ...data } as TUserEncryptionKeys)
|
||||
.onConflict("userId")
|
||||
|
||||
@@ -31,9 +31,7 @@ export const userServiceFactory = ({ userDAL }: TUserServiceFactoryDep) => {
|
||||
if (!user) throw new BadRequestError({ name: "Update auth methods" });
|
||||
|
||||
const hasSamlEnabled = user?.authMethods?.some((method) =>
|
||||
[AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes(
|
||||
method as AuthMethod
|
||||
)
|
||||
[AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes(method as AuthMethod)
|
||||
);
|
||||
if (hasSamlEnabled)
|
||||
throw new BadRequestError({
|
||||
|
||||
@@ -63,12 +63,7 @@ export const webhookDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const findAllWebhooks = async (
|
||||
projectId: string,
|
||||
environment?: string,
|
||||
secretPath?: string,
|
||||
tx?: Knex
|
||||
) => {
|
||||
const findAllWebhooks = async (projectId: string, environment?: string, secretPath?: string, tx?: Knex) => {
|
||||
try {
|
||||
const webhooks = await (tx || db)(TableName.Webhook)
|
||||
.where(`${TableName.Environment}.projectId`, projectId)
|
||||
@@ -103,9 +98,7 @@ export const webhookDALFactory = (db: TDbClient) => {
|
||||
|
||||
const bulkUpdate = async (data: Array<TWebhooksUpdate & { id: string }>, tx?: Knex) => {
|
||||
try {
|
||||
const queries = data.map(({ id, ...el }) =>
|
||||
(tx || db)(TableName.Webhook).where({ id }).update(el)
|
||||
);
|
||||
const queries = data.map(({ id, ...el }) => (tx || db)(TableName.Webhook).where({ id }).update(el));
|
||||
const docs = await Promise.all(queries);
|
||||
return docs;
|
||||
} catch (error) {
|
||||
|
||||
@@ -44,10 +44,7 @@ export const triggerWebhookRequest = async (
|
||||
});
|
||||
}
|
||||
if (secretKey) {
|
||||
const webhookSign = crypto
|
||||
.createHmac("sha256", secretKey)
|
||||
.update(JSON.stringify(payload))
|
||||
.digest("hex");
|
||||
const webhookSign = crypto.createHmac("sha256", secretKey).update(JSON.stringify(payload)).digest("hex");
|
||||
headers["x-infisical-signature"] = `t=${payload.timestamp};${webhookSign}`;
|
||||
}
|
||||
}
|
||||
@@ -98,10 +95,7 @@ export const fnTriggerWebhook = async ({
|
||||
logger.info("Secret webhook job started", { environment, secretPath, projectId });
|
||||
const webhooksTriggered = await Promise.allSettled(
|
||||
toBeTriggeredHooks.map((hook) =>
|
||||
triggerWebhookRequest(
|
||||
hook,
|
||||
getWebhookPayload("secrets.modified", projectId, environment, secretPath)
|
||||
)
|
||||
triggerWebhookRequest(hook, getWebhookPayload("secrets.modified", projectId, environment, secretPath))
|
||||
)
|
||||
);
|
||||
// filter hooks by status
|
||||
|
||||
@@ -2,10 +2,7 @@ import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { SecretEncryptionAlgo, SecretKeyEncoding, TWebhooksInsert } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { encryptSymmetric, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
@@ -29,11 +26,7 @@ type TWebhookServiceFactoryDep = {
|
||||
|
||||
export type TWebhookServiceFactory = ReturnType<typeof webhookServiceFactory>;
|
||||
|
||||
export const webhookServiceFactory = ({
|
||||
webhookDAL,
|
||||
projectEnvDAL,
|
||||
permissionService
|
||||
}: TWebhookServiceFactoryDep) => {
|
||||
export const webhookServiceFactory = ({ webhookDAL, projectEnvDAL, permissionService }: TWebhookServiceFactoryDep) => {
|
||||
const createWebhook = async ({
|
||||
actor,
|
||||
actorId,
|
||||
@@ -44,10 +37,7 @@ export const webhookServiceFactory = ({
|
||||
webhookSecretKey
|
||||
}: TCreateWebhookDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Webhooks
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Webhooks);
|
||||
const env = await projectEnvDAL.findOne({ projectId, slug: environment });
|
||||
if (!env) throw new BadRequestError({ message: "Env not found" });
|
||||
|
||||
@@ -69,10 +59,7 @@ export const webhookServiceFactory = ({
|
||||
insertDoc.algorithm = SecretEncryptionAlgo.AES_256_GCM;
|
||||
insertDoc.keyEncoding = SecretKeyEncoding.BASE64;
|
||||
} else if (encryptionKey) {
|
||||
const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8(
|
||||
webhookSecretKey,
|
||||
encryptionKey
|
||||
);
|
||||
const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8(webhookSecretKey, encryptionKey);
|
||||
insertDoc.encryptedSecretKey = ciphertext;
|
||||
insertDoc.iv = iv;
|
||||
insertDoc.tag = tag;
|
||||
@@ -89,15 +76,8 @@ export const webhookServiceFactory = ({
|
||||
const webhook = await webhookDAL.findById(id);
|
||||
if (!webhook) throw new BadRequestError({ message: "Webhook not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
webhook.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.Webhooks
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, webhook.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Webhooks);
|
||||
|
||||
const updatedWebhook = await webhookDAL.updateById(id, { isDisabled });
|
||||
return { ...webhook, ...updatedWebhook };
|
||||
@@ -107,15 +87,8 @@ export const webhookServiceFactory = ({
|
||||
const webhook = await webhookDAL.findById(id);
|
||||
if (!webhook) throw new BadRequestError({ message: "Webhook not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
webhook.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.Webhooks
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, webhook.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Webhooks);
|
||||
|
||||
const deletedWebhook = await webhookDAL.deleteById(id);
|
||||
return { ...webhook, ...deletedWebhook };
|
||||
@@ -125,15 +98,8 @@ export const webhookServiceFactory = ({
|
||||
const webhook = await webhookDAL.findById(id);
|
||||
if (!webhook) throw new BadRequestError({ message: "Webhook not found" });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
webhook.projectId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Webhooks
|
||||
);
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, webhook.projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);
|
||||
|
||||
let webhookError: string | undefined;
|
||||
try {
|
||||
@@ -152,18 +118,9 @@ export const webhookServiceFactory = ({
|
||||
return { ...webhook, ...updatedWebhook };
|
||||
};
|
||||
|
||||
const listWebhooks = async ({
|
||||
actorId,
|
||||
actor,
|
||||
projectId,
|
||||
secretPath,
|
||||
environment
|
||||
}: TListWebhookDTO) => {
|
||||
const listWebhooks = async ({ actorId, actor, projectId, secretPath, environment }: TListWebhookDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.Webhooks
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);
|
||||
|
||||
return webhookDAL.findAllWebhooks(projectId, environment, secretPath);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user