mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 15:41:27 +00:00
Add org-scoped auth to org-level endpoints, add migration file for org enableAuth field
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { WebhookEventMap } from "@octokit/webhooks-types";
|
||||
import { ProbotOctokit } from "probot";
|
||||
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { UnauthorizedError } from "@app/lib/errors";
|
||||
|
||||
import { TGitAppDALFactory } from "./git-app-dal";
|
||||
import { TGitAppInstallSessionDALFactory } from "./git-app-install-session-dal";
|
||||
import { TSecretScanningDALFactory } from "./secret-scanning-dal";
|
||||
import { TSecretScanningQueueFactory } from "./secret-scanning-queue";
|
||||
import {
|
||||
SecretScanningRiskStatus,
|
||||
TGetOrgInstallStatusDTO,
|
||||
TGetOrgRisksDTO,
|
||||
TInstallAppSessionDTO,
|
||||
TLinkInstallSessionDTO,
|
||||
TUpdateRiskStatusDTO
|
||||
} from "./secret-scanning-types";
|
||||
|
||||
type TSecretScanningServiceFactoryDep = {
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
secretScanningDAL: TSecretScanningDALFactory;
|
||||
gitAppInstallSessionDAL: TGitAppInstallSessionDALFactory;
|
||||
gitAppOrgDAL: TGitAppDALFactory;
|
||||
secretScanningQueue: TSecretScanningQueueFactory;
|
||||
};
|
||||
|
||||
export type TSecretScanningServiceFactory = ReturnType<typeof secretScanningServiceFactory>;
|
||||
|
||||
export const secretScanningServiceFactory = ({
|
||||
secretScanningDAL,
|
||||
gitAppOrgDAL,
|
||||
gitAppInstallSessionDAL,
|
||||
permissionService,
|
||||
secretScanningQueue
|
||||
}: TSecretScanningServiceFactoryDep) => {
|
||||
const createInstallationSession = async ({ actor, orgId, actorId, actorOrgScope }: TInstallAppSessionDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgScope);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
|
||||
|
||||
const sessionId = crypto.randomBytes(16).toString("hex");
|
||||
await gitAppInstallSessionDAL.upsert({ orgId, sessionId, userId: actorId });
|
||||
return { sessionId };
|
||||
};
|
||||
|
||||
const linkInstallationToOrg = async ({ sessionId, actorId, installationId, actor, actorOrgScope }: TLinkInstallSessionDTO) => {
|
||||
const session = await gitAppInstallSessionDAL.findOne({ sessionId });
|
||||
if (!session) throw new UnauthorizedError({ message: "Session not found" });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, session.orgId, actorOrgScope);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
|
||||
const installatedApp = await gitAppOrgDAL.transaction(async (tx) => {
|
||||
await gitAppInstallSessionDAL.deleteById(session.id, tx);
|
||||
return gitAppOrgDAL.upsert({ orgId: session.orgId, installationId, userId: actorId }, tx);
|
||||
});
|
||||
|
||||
const appCfg = getConfig();
|
||||
const octokit = new ProbotOctokit({
|
||||
auth: {
|
||||
appId: appCfg.SECRET_SCANNING_GIT_APP_ID,
|
||||
privateKey: appCfg.SECRET_SCANNING_PRIVATE_KEY,
|
||||
installationId: installationId.toString()
|
||||
}
|
||||
});
|
||||
|
||||
const {
|
||||
data: { repositories }
|
||||
} = await octokit.apps.listReposAccessibleToInstallation();
|
||||
await Promise.all(
|
||||
repositories.map(({ id, full_name }) =>
|
||||
secretScanningQueue.startFullRepoScan({
|
||||
organizationId: session.orgId,
|
||||
installationId,
|
||||
repository: { id, fullName: full_name }
|
||||
})
|
||||
)
|
||||
);
|
||||
return { installatedApp };
|
||||
};
|
||||
|
||||
const getOrgInstallationStatus = async ({ actorId, orgId, actor, actorOrgScope }: TGetOrgInstallStatusDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgScope);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
|
||||
|
||||
const appInstallation = await gitAppOrgDAL.findOne({ orgId });
|
||||
return Boolean(appInstallation);
|
||||
};
|
||||
|
||||
const getRisksByOrg = async ({ actor, orgId, actorId, actorOrgScope }: TGetOrgRisksDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgScope);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
|
||||
const risks = await secretScanningDAL.find({ orgId }, { sort: [["createdAt", "desc"]] });
|
||||
return { risks };
|
||||
};
|
||||
|
||||
const updateRiskStatus = async ({ actorId, orgId, actor, actorOrgScope, riskId, status }: TUpdateRiskStatusDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgScope);
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning);
|
||||
|
||||
const isRiskResolved = Boolean(
|
||||
[
|
||||
SecretScanningRiskStatus.FalsePositive,
|
||||
SecretScanningRiskStatus.Revoked,
|
||||
SecretScanningRiskStatus.NotRevoked
|
||||
].includes(status)
|
||||
);
|
||||
|
||||
const risk = await secretScanningDAL.updateById(riskId, {
|
||||
status,
|
||||
isResolved: isRiskResolved
|
||||
});
|
||||
return { risk };
|
||||
};
|
||||
|
||||
const handleRepoPushEvent = async (payload: WebhookEventMap["push"]) => {
|
||||
const { commits, repository, installation, pusher } = payload;
|
||||
if (!commits || !repository || !installation || !pusher) {
|
||||
return;
|
||||
}
|
||||
|
||||
const installationLink = await gitAppOrgDAL.findOne({
|
||||
installationId: String(installation.id)
|
||||
});
|
||||
if (!installationLink) return;
|
||||
|
||||
await secretScanningQueue.startPushEventScan({
|
||||
commits,
|
||||
pusher: { name: pusher.name, email: pusher.email },
|
||||
repository: { fullName: repository.full_name, id: repository.id },
|
||||
organizationId: installationLink.orgId,
|
||||
installationId: String(installation?.id)
|
||||
});
|
||||
};
|
||||
|
||||
const handleRepoDeleteEvent = async (installationId: string, repositoryIds: string[]) => {
|
||||
await secretScanningDAL.transaction(async (tx) => {
|
||||
if (repositoryIds.length) {
|
||||
await Promise.all(repositoryIds.map((repoId) => secretScanningDAL.delete({ repositoryId: repoId }, tx)));
|
||||
}
|
||||
await gitAppOrgDAL.delete({ installationId }, tx);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
createInstallationSession,
|
||||
linkInstallationToOrg,
|
||||
getOrgInstallationStatus,
|
||||
getRisksByOrg,
|
||||
updateRiskStatus,
|
||||
handleRepoPushEvent,
|
||||
handleRepoDeleteEvent
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user