fix: move permissions

This commit is contained in:
Daniel Hougaard
2025-03-05 22:47:40 +04:00
parent f5dbbaf1fd
commit 16c1516979
3 changed files with 128 additions and 75 deletions
@@ -2114,16 +2114,26 @@ export const secretV2BridgeServiceFactory = ({
[`${TableName.SecretV2}.id` as "id"]: secretIds [`${TableName.SecretV2}.id` as "id"]: secretIds
} }
}); });
const sourceActions = [
ProjectPermissionSecretActions.Delete,
ProjectPermissionSecretActions.ReadValue,
ProjectPermissionSecretActions.DescribeSecret
] as const;
const destinationActions = [ProjectPermissionSecretActions.Create, ProjectPermissionSecretActions.Edit] as const;
sourceSecrets.forEach((secret) => { sourceSecrets.forEach((secret) => {
ForbiddenError.from(permission).throwUnlessCan( for (const sourceAction of sourceActions) {
ProjectPermissionSecretActions.Delete, ForbiddenError.from(permission).throwUnlessCan(
subject(ProjectPermissionSub.Secrets, { sourceAction,
environment: sourceEnvironment, subject(ProjectPermissionSub.Secrets, {
secretPath: sourceSecretPath, environment: sourceEnvironment,
secretName: secret.key, secretPath: sourceSecretPath,
secretTags: secret.tags.map((el) => el.slug) secretName: secret.key,
}) secretTags: secret.tags.map((el) => el.slug)
); })
);
}
}); });
if (sourceSecrets.length !== secretIds.length) { if (sourceSecrets.length !== secretIds.length) {
@@ -2198,27 +2208,17 @@ export const secretV2BridgeServiceFactory = ({
// permission check whether can create or edit the ones in the destination folder // permission check whether can create or edit the ones in the destination folder
locallyCreatedSecrets.forEach((secret) => { locallyCreatedSecrets.forEach((secret) => {
ForbiddenError.from(permission).throwUnlessCan( for (const destinationAction of destinationActions) {
ProjectPermissionSecretActions.Create, ForbiddenError.from(permission).throwUnlessCan(
subject(ProjectPermissionSub.Secrets, { destinationAction,
environment: destinationEnvironment, subject(ProjectPermissionSub.Secrets, {
secretPath: destinationEnvironment, environment: destinationEnvironment,
secretName: secret.key, secretPath: destinationFolder.path,
secretTags: secret.tags.map((el) => el.slug) secretName: secret.key,
}) secretTags: secret.tags.map((el) => el.slug)
); })
}); );
}
locallyUpdatedSecrets.forEach((secret) => {
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionSecretActions.Edit,
subject(ProjectPermissionSub.Secrets, {
environment: destinationEnvironment,
secretPath: destinationEnvironment,
secretName: secret.key,
secretTags: secret.tags.map((el) => el.slug)
})
);
}); });
const destinationFolderPolicy = await secretApprovalPolicyService.getSecretApprovalPolicy( const destinationFolderPolicy = await secretApprovalPolicyService.getSecretApprovalPolicy(
+44 -1
View File
@@ -169,6 +169,48 @@ export const secretDALFactory = (db: TDbClient) => {
} }
}; };
const findManySecretsWithTags = async (
filter: {
secretIds: string[];
type: SecretType;
},
tx?: Knex
) => {
try {
const secrets = await (tx || db.replicaNode())(TableName.Secret)
.whereIn(`${TableName.Secret}.id` as "id", filter.secretIds)
.where("type", filter.type)
.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"))
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"));
const data = sqlNestRelationships({
data: secrets,
key: "id",
parentMapper: (el) => ({ _id: el.id, ...SecretsSchema.parse(el) }),
childrenMapper: [
{
key: "tagId",
label: "tags" as const,
mapper: ({ tagId: id, tagColor: color, tagSlug: slug }) => ({
id,
color,
slug,
name: slug
})
}
]
});
return data;
} catch (error) {
throw new DatabaseError({ error, name: "get many secrets with tags" });
}
};
const findByFolderIds = async (folderIds: string[], userId?: string, tx?: Knex) => { const findByFolderIds = async (folderIds: string[], userId?: string, tx?: Knex) => {
try { try {
// check if not uui then userId id is null (corner case because service token's ID is not UUI in effort to keep backwards compatibility from mongo) // check if not uui then userId id is null (corner case because service token's ID is not UUI in effort to keep backwards compatibility from mongo)
@@ -443,6 +485,7 @@ export const secretDALFactory = (db: TDbClient) => {
upsertSecretReferences, upsertSecretReferences,
findReferencedSecretReferences, findReferencedSecretReferences,
findAllProjectSecretValues, findAllProjectSecretValues,
pruneSecretReminders pruneSecretReminders,
findManySecretsWithTags
}; };
}; };
+54 -44
View File
@@ -6,6 +6,7 @@ import {
ActionProjectType, ActionProjectType,
ProjectMembershipRole, ProjectMembershipRole,
ProjectUpgradeStatus, ProjectUpgradeStatus,
ProjectVersion,
SecretEncryptionAlgo, SecretEncryptionAlgo,
SecretKeyEncoding, SecretKeyEncoding,
SecretsSchema, SecretsSchema,
@@ -2730,7 +2731,7 @@ export const secretServiceFactory = ({
message: `Project with slug '${projectSlug}' not found` message: `Project with slug '${projectSlug}' not found`
}); });
} }
if (project.version === 3) { if (project.version === ProjectVersion.V3) {
return secretV2BridgeService.moveSecrets({ return secretV2BridgeService.moveSecrets({
sourceEnvironment, sourceEnvironment,
sourceSecretPath, sourceSecretPath,
@@ -2755,34 +2756,6 @@ export const secretServiceFactory = ({
actionProjectType: ActionProjectType.SecretManager actionProjectType: ActionProjectType.SecretManager
}); });
const permissionChecks = [
{
action: ProjectPermissionSecretActions.Delete,
subject: {
environment: sourceEnvironment,
secretPath: sourceSecretPath
}
},
{
action: ProjectPermissionSecretActions.Create,
subject: {
environment: destinationEnvironment,
secretPath: destinationSecretPath
}
},
{
action: ProjectPermissionSecretActions.Edit,
subject: {
environment: destinationEnvironment,
secretPath: destinationSecretPath
}
}
] as const;
for (const { action, subject: permissionSubject } of permissionChecks) {
ForbiddenError.from(permission).throwUnlessCan(action, subject(ProjectPermissionSub.Secrets, permissionSubject));
}
const { botKey } = await projectBotService.getBotKey(project.id); const { botKey } = await projectBotService.getBotKey(project.id);
if (!botKey) { if (!botKey) {
throw new NotFoundError({ throw new NotFoundError({
@@ -2810,11 +2783,9 @@ export const secretServiceFactory = ({
}); });
} }
const sourceSecrets = await secretDAL.find({ const sourceSecrets = await secretDAL.findManySecretsWithTags({
type: SecretType.Shared, type: SecretType.Shared,
$in: { secretIds
id: secretIds
}
}); });
if (sourceSecrets.length !== secretIds.length) { if (sourceSecrets.length !== secretIds.length) {
@@ -2823,21 +2794,60 @@ export const secretServiceFactory = ({
}); });
} }
const decryptedSourceSecrets = sourceSecrets.map((secret) => ({ const sourceActions = [
...secret, ProjectPermissionSecretActions.Delete,
secretKey: decryptSymmetric128BitHexKeyUTF8({ ProjectPermissionSecretActions.DescribeSecret,
ProjectPermissionSecretActions.ReadValue
] as const;
const destinationActions = [ProjectPermissionSecretActions.Create, ProjectPermissionSecretActions.Edit] as const;
const decryptedSourceSecrets = sourceSecrets.map((secret) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretKeyCiphertext, ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV, iv: secret.secretKeyIV,
tag: secret.secretKeyTag, tag: secret.secretKeyTag,
key: botKey key: botKey
}), });
secretValue: decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretValueCiphertext, for (const destinationAction of destinationActions) {
iv: secret.secretValueIV, ForbiddenError.from(permission).throwUnlessCan(
tag: secret.secretValueTag, destinationAction,
key: botKey subject(ProjectPermissionSub.Secrets, {
}) environment: destinationEnvironment,
})); secretPath: destinationSecretPath,
secretName: secretKey,
...(secret.tags.length && {
secretTags: secret.tags.map((t) => t.id)
})
})
);
}
for (const sourceAction of sourceActions) {
ForbiddenError.from(permission).throwUnlessCan(
sourceAction,
subject(ProjectPermissionSub.Secrets, {
environment: sourceEnvironment,
secretPath: sourceSecretPath,
secretName: secretKey,
...(secret.tags.length && {
secretTags: secret.tags.map((t) => t.id)
})
})
);
}
return {
...secret,
secretKey,
secretValue: decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key: botKey
})
};
});
let isSourceUpdated = false; let isSourceUpdated = false;
let isDestinationUpdated = false; let isDestinationUpdated = false;