Continue secret sharing

This commit is contained in:
Tuan Dang
2024-07-29 14:32:11 -07:00
parent 938a7b7e72
commit c041e44399
32 changed files with 795 additions and 547 deletions
@@ -1,5 +1,5 @@
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
import { SecretSharingAccessType } from "@app/lib/types";
import { TOrgDALFactory } from "../org/org-dal";
@@ -8,7 +8,7 @@ import {
TCreatePublicSharedSecretDTO,
TCreateSharedSecretDTO,
TDeleteSharedSecretDTO,
TSharedSecretPermission
TGetSharedSecretsDTO
} from "./secret-sharing-types";
type TSecretSharingServiceFactoryDep = {
@@ -34,8 +34,8 @@ export const secretSharingServiceFactory = ({
encryptedValue,
iv,
tag,
name,
accessType,
hashedHex,
expiresAt,
expiresAfterViews
} = createSharedSecretInput;
@@ -60,10 +60,10 @@ export const secretSharingServiceFactory = ({
}
const newSharedSecret = await secretSharingDAL.create({
name,
encryptedValue,
iv,
tag,
hashedHex,
expiresAt: new Date(expiresAt),
expiresAfterViews,
userId: actorId,
@@ -75,7 +75,7 @@ export const secretSharingServiceFactory = ({
};
const createPublicSharedSecret = async (createSharedSecretInput: TCreatePublicSharedSecretDTO) => {
const { encryptedValue, iv, tag, hashedHex, expiresAt, expiresAfterViews, accessType } = createSharedSecretInput;
const { encryptedValue, iv, tag, expiresAt, expiresAfterViews, accessType } = createSharedSecretInput;
if (new Date(expiresAt) < new Date()) {
throw new BadRequestError({ message: "Expiration date cannot be in the past" });
}
@@ -97,7 +97,6 @@ export const secretSharingServiceFactory = ({
encryptedValue,
iv,
tag,
hashedHex,
expiresAt: new Date(expiresAt),
expiresAfterViews,
accessType
@@ -105,43 +104,90 @@ export const secretSharingServiceFactory = ({
return { id: newSharedSecret.id };
};
const getSharedSecrets = async (getSharedSecretsInput: TSharedSecretPermission) => {
const { actor, actorId, orgId, actorAuthMethod, actorOrgId } = getSharedSecretsInput;
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
const getSharedSecrets = async ({
actor,
actorId,
actorAuthMethod,
actorOrgId,
offset,
limit
}: TGetSharedSecretsDTO) => {
if (!actorOrgId) throw new BadRequestError({ message: "Failed to create group without organization" });
const { permission } = await permissionService.getOrgPermission(
actor,
actorId,
actorOrgId,
actorAuthMethod,
actorOrgId
);
if (!permission) throw new UnauthorizedError({ name: "User not in org" });
const userSharedSecrets = await secretSharingDAL.findActiveSharedSecrets({ userId: actorId, orgId });
return userSharedSecrets;
const secrets = await secretSharingDAL.find(
{
userId: actorId,
orgId: actorOrgId
},
{ offset, limit, sort: [["createdAt", "desc"]] }
);
const count = await secretSharingDAL.countAllUserOrgSharedSecrets({
orgId: actorOrgId,
userId: actorId
});
return {
secrets,
totalCount: count
};
};
const getActiveSharedSecretByIdAndHashedHex = async (sharedSecretId: string, hashedHex: string, orgId?: string) => {
const sharedSecret = await secretSharingDAL.findOne({ id: sharedSecretId, hashedHex });
if (!sharedSecret) return;
const getActiveSharedSecretById = async (sharedSecretId: string, orgId?: string) => {
const sharedSecret = await secretSharingDAL.findOne({ id: sharedSecretId });
if (!sharedSecret)
throw new NotFoundError({
message: "Shared secret not found"
});
const { accessType, expiresAt, expiresAfterViews } = sharedSecret;
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 (accessType === SecretSharingAccessType.Organization && orgId !== sharedSecret.orgId)
throw new UnauthorizedError();
if (expiresAt !== null && expiresAt < new Date()) {
// check lifetime expiry
await secretSharingDAL.softDeleteById(sharedSecretId);
throw new ForbiddenRequestError({
message: "Access denied: Secret has expired by lifetime"
});
}
if (sharedSecret.expiresAt && sharedSecret.expiresAt < new Date()) {
return;
if (expiresAfterViews !== null && expiresAfterViews === 0) {
// check view count expiry
await secretSharingDAL.softDeleteById(sharedSecretId);
throw new ForbiddenRequestError({
message: "Access denied: Secret has expired by view count"
});
}
if (sharedSecret.expiresAfterViews != null && sharedSecret.expiresAfterViews >= 0) {
if (sharedSecret.expiresAfterViews === 0) {
await secretSharingDAL.softDeleteById(sharedSecretId);
return;
}
if (expiresAfterViews) {
// decrement view count if view count expiry set
await secretSharingDAL.updateById(sharedSecretId, { $decr: { expiresAfterViews: 1 } });
}
if (sharedSecret.accessType === SecretSharingAccessType.Organization && orgId === sharedSecret.orgId) {
return { ...sharedSecret, orgName };
}
return { ...sharedSecret, orgName: undefined };
await secretSharingDAL.updateById(sharedSecretId, {
lastViewedAt: new Date()
});
return {
...sharedSecret,
orgName:
sharedSecret.accessType === SecretSharingAccessType.Organization && orgId === sharedSecret.orgId
? orgName
: undefined
};
};
const deleteSharedSecretById = async (deleteSharedSecretInput: TDeleteSharedSecretDTO) => {
@@ -157,6 +203,6 @@ export const secretSharingServiceFactory = ({
createPublicSharedSecret,
getSharedSecrets,
deleteSharedSecretById,
getActiveSharedSecretByIdAndHashedHex
getActiveSharedSecretById
};
};