mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat(backend): Adding accessType on secret sharing
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { SecretSharingAccessType } from "@app/lib/types";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const hasColumn = await knex.schema.hasColumn(TableName.SecretSharing, "accessType");
|
||||
if (!hasColumn) {
|
||||
await knex.schema.table(TableName.SecretSharing, (table) => {
|
||||
table.string("accessType", 20).notNullable().defaultTo(SecretSharingAccessType.Anyone);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const hasColumn = await knex.schema.hasColumn(TableName.SecretSharing, "accessType");
|
||||
if (hasColumn) {
|
||||
await knex.schema.table(TableName.SecretSharing, (table) => {
|
||||
table.dropColumn("accessType");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretSharingAccessType } from "@app/lib/types";
|
||||
|
||||
import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const SecretSharingSchema = z.object({
|
||||
@@ -16,6 +18,7 @@ export const SecretSharingSchema = z.object({
|
||||
expiresAt: z.date(),
|
||||
userId: z.string().uuid().nullable().optional(),
|
||||
orgId: z.string().uuid().nullable().optional(),
|
||||
accessType: z.nativeEnum(SecretSharingAccessType).default(SecretSharingAccessType.Organization),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
expiresAfterViews: z.number().nullable().optional()
|
||||
|
||||
@@ -47,3 +47,8 @@ export enum EnforcementLevel {
|
||||
Hard = "hard",
|
||||
Soft = "soft"
|
||||
}
|
||||
|
||||
export enum SecretSharingAccessType {
|
||||
Anyone = "anyone",
|
||||
Organization = "organization"
|
||||
}
|
||||
|
||||
@@ -737,7 +737,8 @@ export const registerRoutes = async (
|
||||
|
||||
const secretSharingService = secretSharingServiceFactory({
|
||||
permissionService,
|
||||
secretSharingDAL
|
||||
secretSharingDAL,
|
||||
orgDAL
|
||||
});
|
||||
|
||||
const secretApprovalRequestService = secretApprovalRequestServiceFactory({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretSharingSchema } from "@app/db/schemas";
|
||||
import { SecretSharingAccessType } from "@app/lib/types";
|
||||
import {
|
||||
publicEndpointLimit,
|
||||
publicSecretShareCreationLimit,
|
||||
@@ -55,14 +56,18 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
|
||||
iv: true,
|
||||
tag: true,
|
||||
expiresAt: true,
|
||||
expiresAfterViews: true
|
||||
expiresAfterViews: true,
|
||||
accessType: true
|
||||
}).extend({
|
||||
orgName: z.string().optional()
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const sharedSecret = await req.server.services.secretSharing.getActiveSharedSecretByIdAndHashedHex(
|
||||
req.params.id,
|
||||
req.query.hashedHex
|
||||
req.query.hashedHex,
|
||||
req.permission?.orgId
|
||||
);
|
||||
if (!sharedSecret) return undefined;
|
||||
return {
|
||||
@@ -70,7 +75,9 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
|
||||
iv: sharedSecret.iv,
|
||||
tag: sharedSecret.tag,
|
||||
expiresAt: sharedSecret.expiresAt,
|
||||
expiresAfterViews: sharedSecret.expiresAfterViews
|
||||
expiresAfterViews: sharedSecret.expiresAfterViews,
|
||||
accessType: sharedSecret.accessType,
|
||||
orgName: sharedSecret.orgName
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -88,7 +95,8 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
|
||||
tag: z.string(),
|
||||
hashedHex: z.string(),
|
||||
expiresAt: z.string(),
|
||||
expiresAfterViews: z.number()
|
||||
expiresAfterViews: z.number(),
|
||||
accessType: z.nativeEnum(SecretSharingAccessType).default(SecretSharingAccessType.Organization)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -97,14 +105,15 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const { encryptedValue, iv, tag, hashedHex, expiresAt, expiresAfterViews } = req.body;
|
||||
const { encryptedValue, iv, tag, hashedHex, expiresAt, expiresAfterViews, accessType } = req.body;
|
||||
const sharedSecret = await req.server.services.secretSharing.createPublicSharedSecret({
|
||||
encryptedValue,
|
||||
iv,
|
||||
tag,
|
||||
hashedHex,
|
||||
expiresAt: new Date(expiresAt),
|
||||
expiresAfterViews
|
||||
expiresAfterViews,
|
||||
accessType
|
||||
});
|
||||
return { id: sharedSecret.id };
|
||||
}
|
||||
@@ -123,7 +132,8 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
|
||||
tag: z.string(),
|
||||
hashedHex: z.string(),
|
||||
expiresAt: z.string(),
|
||||
expiresAfterViews: z.number()
|
||||
expiresAfterViews: z.number(),
|
||||
accessType: z.nativeEnum(SecretSharingAccessType).default(SecretSharingAccessType.Organization)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -145,7 +155,8 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
|
||||
tag,
|
||||
hashedHex,
|
||||
expiresAt: new Date(expiresAt),
|
||||
expiresAfterViews
|
||||
expiresAfterViews,
|
||||
accessType: req.body.accessType
|
||||
});
|
||||
return { id: sharedSecret.id };
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
import { SecretSharingAccessType } from "@app/lib/types";
|
||||
|
||||
import { TOrgDALFactory } from "../org/org-dal";
|
||||
import { TSecretSharingDALFactory } from "./secret-sharing-dal";
|
||||
import {
|
||||
TCreatePublicSharedSecretDTO,
|
||||
@@ -12,13 +14,15 @@ import {
|
||||
type TSecretSharingServiceFactoryDep = {
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
secretSharingDAL: TSecretSharingDALFactory;
|
||||
orgDAL: TOrgDALFactory;
|
||||
};
|
||||
|
||||
export type TSecretSharingServiceFactory = ReturnType<typeof secretSharingServiceFactory>;
|
||||
|
||||
export const secretSharingServiceFactory = ({
|
||||
permissionService,
|
||||
secretSharingDAL
|
||||
secretSharingDAL,
|
||||
orgDAL
|
||||
}: TSecretSharingServiceFactoryDep) => {
|
||||
const createSharedSecret = async (createSharedSecretInput: TCreateSharedSecretDTO) => {
|
||||
const {
|
||||
@@ -30,6 +34,7 @@ export const secretSharingServiceFactory = ({
|
||||
encryptedValue,
|
||||
iv,
|
||||
tag,
|
||||
accessType,
|
||||
hashedHex,
|
||||
expiresAt,
|
||||
expiresAfterViews
|
||||
@@ -62,13 +67,14 @@ export const secretSharingServiceFactory = ({
|
||||
expiresAt,
|
||||
expiresAfterViews,
|
||||
userId: actorId,
|
||||
orgId
|
||||
orgId,
|
||||
accessType
|
||||
});
|
||||
return { id: newSharedSecret.id };
|
||||
};
|
||||
|
||||
const createPublicSharedSecret = async (createSharedSecretInput: TCreatePublicSharedSecretDTO) => {
|
||||
const { encryptedValue, iv, tag, hashedHex, expiresAt, expiresAfterViews } = createSharedSecretInput;
|
||||
const { encryptedValue, iv, tag, hashedHex, expiresAt, expiresAfterViews, accessType } = createSharedSecretInput;
|
||||
if (new Date(expiresAt) < new Date()) {
|
||||
throw new BadRequestError({ message: "Expiration date cannot be in the past" });
|
||||
}
|
||||
@@ -92,7 +98,8 @@ export const secretSharingServiceFactory = ({
|
||||
tag,
|
||||
hashedHex,
|
||||
expiresAt,
|
||||
expiresAfterViews
|
||||
expiresAfterViews,
|
||||
accessType
|
||||
});
|
||||
return { id: newSharedSecret.id };
|
||||
};
|
||||
@@ -105,9 +112,21 @@ export const secretSharingServiceFactory = ({
|
||||
return userSharedSecrets;
|
||||
};
|
||||
|
||||
const getActiveSharedSecretByIdAndHashedHex = async (sharedSecretId: string, hashedHex: string) => {
|
||||
const getActiveSharedSecretByIdAndHashedHex = async (sharedSecretId: string, hashedHex: string, orgId?: string) => {
|
||||
const sharedSecret = await secretSharingDAL.findOne({ id: sharedSecretId, hashedHex });
|
||||
if (!sharedSecret) return;
|
||||
|
||||
const orgName = sharedSecret.orgId ? (await orgDAL.findOrgById(sharedSecret.orgId))?.name : "";
|
||||
// Support organization level access for secret sharing
|
||||
if (sharedSecret.accessType === SecretSharingAccessType.Organization && orgId !== sharedSecret.orgId) {
|
||||
return {
|
||||
...sharedSecret,
|
||||
encryptedValue: "",
|
||||
iv: "",
|
||||
tag: "",
|
||||
orgName
|
||||
};
|
||||
}
|
||||
if (sharedSecret.expiresAt && sharedSecret.expiresAt < new Date()) {
|
||||
return;
|
||||
}
|
||||
@@ -118,7 +137,10 @@ export const secretSharingServiceFactory = ({
|
||||
}
|
||||
await secretSharingDAL.updateById(sharedSecretId, { $decr: { expiresAfterViews: 1 } });
|
||||
}
|
||||
return sharedSecret;
|
||||
if (sharedSecret.accessType === SecretSharingAccessType.Organization) {
|
||||
return { ...sharedSecret, orgName };
|
||||
}
|
||||
return { ...sharedSecret, orgName: undefined };
|
||||
};
|
||||
|
||||
const deleteSharedSecretById = async (deleteSharedSecretInput: TDeleteSharedSecretDTO) => {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { SecretSharingAccessType } from "@app/lib/types";
|
||||
|
||||
import { ActorAuthMethod, ActorType } from "../auth/auth-type";
|
||||
|
||||
export type TSharedSecretPermission = {
|
||||
@@ -6,6 +8,7 @@ export type TSharedSecretPermission = {
|
||||
actorAuthMethod: ActorAuthMethod;
|
||||
actorOrgId: string;
|
||||
orgId: string;
|
||||
accessType?: SecretSharingAccessType;
|
||||
};
|
||||
|
||||
export type TCreatePublicSharedSecretDTO = {
|
||||
@@ -15,6 +18,7 @@ export type TCreatePublicSharedSecretDTO = {
|
||||
hashedHex: string;
|
||||
expiresAt: Date;
|
||||
expiresAfterViews: number;
|
||||
accessType: SecretSharingAccessType;
|
||||
};
|
||||
|
||||
export type TCreateSharedSecretDTO = TSharedSecretPermission & TCreatePublicSharedSecretDTO;
|
||||
|
||||
Reference in New Issue
Block a user