fix: requested changes

This commit is contained in:
Daniel Hougaard
2025-03-05 22:47:40 +04:00
parent f0b05c683b
commit f28f4f7561
2 changed files with 151 additions and 141 deletions
@@ -16,7 +16,7 @@ enum SecretActions {
ReadValue = "readValue" ReadValue = "readValue"
} }
export const UnpackedPermissionSchema = z.object({ const UnpackedPermissionSchema = z.object({
subject: z subject: z
.union([z.string().min(1), z.string().array()]) .union([z.string().min(1), z.string().array()])
.transform((el) => (typeof el !== "string" ? el[0] : el)) .transform((el) => (typeof el !== "string" ? el[0] : el))
@@ -26,9 +26,59 @@ export const UnpackedPermissionSchema = z.object({
inverted: z.boolean().optional() inverted: z.boolean().optional()
}); });
export const unpackPermissions = (permissions: unknown) => const $unpackPermissions = (permissions: unknown) =>
UnpackedPermissionSchema.array().parse(unpackRules((permissions || []) as PackRule<RawRuleOf<MongoAbility>>[])); UnpackedPermissionSchema.array().parse(unpackRules((permissions || []) as PackRule<RawRuleOf<MongoAbility>>[]));
const $updatePermissionsUp = (permissions: unknown) => {
const parsedPermissions = $unpackPermissions(permissions);
let shouldUpdate = false;
for (let i = 0; i < parsedPermissions.length; i += 1) {
const parsedPermission = parsedPermissions[i];
const { subject, action } = parsedPermission;
if (subject === ProjectPermissionSub.Secrets) {
if (action.includes(SecretActions.Read) && !action.includes(SecretActions.ReadValue)) {
action.push(SecretActions.ReadValue);
parsedPermissions[i] = { ...parsedPermission, action };
shouldUpdate = true;
}
}
}
return {
parsedPermissions,
shouldUpdate
};
};
const $updatePermissionsDown = (permissions: unknown) => {
const parsedPermissions = $unpackPermissions(permissions);
let shouldUpdate = false;
for (let i = 0; i < parsedPermissions.length; i += 1) {
const parsedPermission = parsedPermissions[i];
const { subject, action } = parsedPermission;
if (subject === ProjectPermissionSub.Secrets) {
if (action.includes(SecretActions.ReadValue)) {
action.splice(action.indexOf(SecretActions.ReadValue));
parsedPermissions[i] = { ...parsedPermission, action };
shouldUpdate = true;
}
}
}
const repackedPermissions = packRules(parsedPermissions);
return {
repackedPermissions,
shouldUpdate
};
};
export async function up(knex: Knex): Promise<void> { export async function up(knex: Knex): Promise<void> {
const projectRoles = await knex(TableName.ProjectRoles).select(selectAllTableCols(TableName.ProjectRoles)); const projectRoles = await knex(TableName.ProjectRoles).select(selectAllTableCols(TableName.ProjectRoles));
const projectIdentityAdditionalPrivileges = await knex(TableName.IdentityProjectAdditionalPrivilege).select( const projectIdentityAdditionalPrivileges = await knex(TableName.IdentityProjectAdditionalPrivilege).select(
@@ -38,88 +88,62 @@ export async function up(knex: Knex): Promise<void> {
selectAllTableCols(TableName.ProjectUserAdditionalPrivilege) selectAllTableCols(TableName.ProjectUserAdditionalPrivilege)
); );
for await (const projectRole of projectRoles) { const updatedRoles = projectRoles.reduce<typeof projectRoles>((acc, projectRole) => {
const { permissions } = projectRole; const { shouldUpdate, parsedPermissions } = $updatePermissionsUp(projectRole.permissions);
const parsedPermissions = unpackPermissions(permissions);
let shouldUpdate = false;
for (let i = 0; i < parsedPermissions.length; i += 1) {
const parsedPermission = parsedPermissions[i];
const { subject, action } = parsedPermission;
if (subject === ProjectPermissionSub.Secrets) {
if (action.includes(SecretActions.Read) && !action.includes(SecretActions.ReadValue)) {
action.push(SecretActions.ReadValue);
parsedPermissions[i] = { ...parsedPermission, action };
shouldUpdate = true;
}
}
}
if (shouldUpdate) { if (shouldUpdate) {
const repackedPermissions = packRules(parsedPermissions); acc.push({
...projectRole,
await knex(TableName.ProjectRoles) permissions: JSON.stringify(packRules(parsedPermissions))
.where("id", projectRole.id) });
.update({ permissions: JSON.stringify(repackedPermissions) });
} }
return acc;
}, []);
const updatedIdentityAdditionalPrivileges = projectIdentityAdditionalPrivileges.reduce<
typeof projectIdentityAdditionalPrivileges
>((acc, identityAdditionalPrivilege) => {
const { shouldUpdate, parsedPermissions } = $updatePermissionsUp(identityAdditionalPrivilege.permissions);
if (shouldUpdate) {
acc.push({
...identityAdditionalPrivilege,
permissions: JSON.stringify(packRules(parsedPermissions))
});
}
return acc;
}, []);
const updatedUserAdditionalPrivileges = projectUserAdditionalPrivileges.reduce<
typeof projectUserAdditionalPrivileges
>((acc, userAdditionalPrivilege) => {
const { shouldUpdate, parsedPermissions } = $updatePermissionsUp(userAdditionalPrivilege.permissions);
if (shouldUpdate) {
acc.push({
...userAdditionalPrivilege,
permissions: JSON.stringify(packRules(parsedPermissions))
});
}
return acc;
}, []);
if (updatedRoles.length > 0) {
await knex(TableName.ProjectRoles).insert(updatedRoles).onConflict("id").merge(["permissions"]);
} }
for await (const identityAdditionalPrivilege of projectIdentityAdditionalPrivileges) { if (updatedIdentityAdditionalPrivileges.length > 0) {
const { permissions } = identityAdditionalPrivilege; await knex(TableName.IdentityProjectAdditionalPrivilege)
.insert(updatedIdentityAdditionalPrivileges)
const parsedPermissions = unpackPermissions(permissions); .onConflict("id")
let shouldUpdate = false; .merge(["permissions"]);
for (let i = 0; i < parsedPermissions.length; i += 1) {
const parsedPermission = parsedPermissions[i];
const { subject, action } = parsedPermission;
if (subject === ProjectPermissionSub.Secrets) {
if (action.includes(SecretActions.Read) && !action.includes(SecretActions.ReadValue)) {
action.push(SecretActions.ReadValue);
parsedPermissions[i] = { ...parsedPermission, action };
shouldUpdate = true;
}
}
}
if (shouldUpdate) {
const repackedPermissions = packRules(parsedPermissions);
await knex(TableName.IdentityProjectAdditionalPrivilege)
.where("id", identityAdditionalPrivilege.id)
.update({ permissions: JSON.stringify(repackedPermissions) });
}
} }
for await (const userAdditionalPrivilege of projectUserAdditionalPrivileges) { if (updatedUserAdditionalPrivileges.length > 0) {
const { permissions } = userAdditionalPrivilege; await knex(TableName.ProjectUserAdditionalPrivilege)
.insert(updatedUserAdditionalPrivileges)
const parsedPermissions = unpackPermissions(permissions); .onConflict("id")
let shouldUpdate = false; .merge(["permissions"]);
for (let i = 0; i < parsedPermissions.length; i += 1) {
const parsedPermission = parsedPermissions[i];
const { subject, action } = parsedPermission;
if (subject === ProjectPermissionSub.Secrets) {
if (action.includes(SecretActions.Read) && !action.includes(SecretActions.ReadValue)) {
action.push(SecretActions.ReadValue);
parsedPermissions[i] = { ...parsedPermission, action };
shouldUpdate = true;
}
}
}
if (shouldUpdate) {
const repackedPermissions = packRules(parsedPermissions);
await knex(TableName.ProjectUserAdditionalPrivilege)
.where("id", userAdditionalPrivilege.id)
.update({ permissions: JSON.stringify(repackedPermissions) });
}
} }
} }
@@ -132,78 +156,63 @@ export async function down(knex: Knex): Promise<void> {
selectAllTableCols(TableName.ProjectUserAdditionalPrivilege) selectAllTableCols(TableName.ProjectUserAdditionalPrivilege)
); );
for await (const projectRole of projectRoles) { const updatedRoles = projectRoles.reduce<typeof projectRoles>((acc, projectRole) => {
const { permissions } = projectRole; const { shouldUpdate, repackedPermissions } = $updatePermissionsDown(projectRole.permissions);
const parsedPermissions = unpackPermissions(permissions); if (shouldUpdate) {
acc.push({
for (let i = 0; i < parsedPermissions.length; i += 1) { ...projectRole,
const parsedPermission = parsedPermissions[i]; permissions: JSON.stringify(repackedPermissions)
});
const { subject, action } = parsedPermission;
if (subject === ProjectPermissionSub.Secrets) {
if (action.includes(SecretActions.ReadValue)) {
action.splice(action.indexOf(SecretActions.ReadValue));
parsedPermissions[i] = { ...parsedPermission, action };
}
}
} }
return acc;
}, []);
const repackedPermissions = packRules(parsedPermissions); const updatedIdentityAdditionalPrivileges = identityAdditionalPrivileges.reduce<typeof identityAdditionalPrivileges>(
(acc, identityAdditionalPrivilege) => {
const { shouldUpdate, repackedPermissions } = $updatePermissionsDown(identityAdditionalPrivilege.permissions);
await knex(TableName.ProjectRoles) if (shouldUpdate) {
.where("id", projectRole.id) acc.push({
.update({ permissions: JSON.stringify(repackedPermissions) }); ...identityAdditionalPrivilege,
permissions: JSON.stringify(repackedPermissions)
});
}
return acc;
},
[]
);
const updatedUserAdditionalPrivileges = userAdditionalPrivileges.reduce<typeof userAdditionalPrivileges>(
(acc, userAdditionalPrivilege) => {
const { shouldUpdate, repackedPermissions } = $updatePermissionsDown(userAdditionalPrivilege.permissions);
if (shouldUpdate) {
acc.push({
...userAdditionalPrivilege,
permissions: JSON.stringify(repackedPermissions)
});
}
return acc;
},
[]
);
if (updatedRoles.length > 0) {
await knex(TableName.ProjectRoles).insert(updatedRoles).onConflict("id").merge(["permissions"]);
} }
for await (const identityAdditionalPrivilege of identityAdditionalPrivileges) { if (updatedIdentityAdditionalPrivileges.length > 0) {
const { permissions } = identityAdditionalPrivilege;
const parsedPermissions = unpackPermissions(permissions);
for (let i = 0; i < parsedPermissions.length; i += 1) {
const parsedPermission = parsedPermissions[i];
const { subject, action } = parsedPermission;
if (subject === ProjectPermissionSub.Secrets) {
if (action.includes(SecretActions.ReadValue)) {
action.splice(action.indexOf(SecretActions.ReadValue));
parsedPermissions[i] = { ...parsedPermission, action };
}
}
}
const repackedPermissions = packRules(parsedPermissions);
await knex(TableName.IdentityProjectAdditionalPrivilege) await knex(TableName.IdentityProjectAdditionalPrivilege)
.where("id", identityAdditionalPrivilege.id) .insert(updatedIdentityAdditionalPrivileges)
.update({ permissions: JSON.stringify(repackedPermissions) }); .onConflict("id")
.merge(["permissions"]);
} }
for await (const userAdditionalPrivilege of userAdditionalPrivileges) { if (updatedUserAdditionalPrivileges.length > 0) {
const { permissions } = userAdditionalPrivilege;
const parsedPermissions = unpackPermissions(permissions);
for (let i = 0; i < parsedPermissions.length; i += 1) {
const parsedPermission = parsedPermissions[i];
const { subject, action } = parsedPermission;
if (subject === ProjectPermissionSub.Secrets) {
if (action.includes(SecretActions.ReadValue)) {
action.splice(action.indexOf(SecretActions.ReadValue));
parsedPermissions[i] = { ...parsedPermission, action };
}
}
}
const repackedPermissions = packRules(parsedPermissions);
await knex(TableName.ProjectUserAdditionalPrivilege) await knex(TableName.ProjectUserAdditionalPrivilege)
.where("id", userAdditionalPrivilege.id) .insert(updatedUserAdditionalPrivileges)
.update({ permissions: JSON.stringify(repackedPermissions) }); .onConflict("id")
.merge(["permissions"]);
} }
} }
@@ -437,11 +437,12 @@ const GeneralPermissionSchema = [
}) })
]; ];
// Do not update this schema anymore, as it's kept purely for backwards compatability. Update V2 schema only.
export const ProjectPermissionV1Schema = z.discriminatedUnion("subject", [ export const ProjectPermissionV1Schema = z.discriminatedUnion("subject", [
z.object({ z.object({
subject: z.literal(ProjectPermissionSub.Secrets).describe("The entity this permission pertains to."), subject: z.literal(ProjectPermissionSub.Secrets).describe("The entity this permission pertains to."),
inverted: z.boolean().optional().describe("Whether rule allows or forbids."), inverted: z.boolean().optional().describe("Whether rule allows or forbids."),
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionSecretActions).describe( action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe(
"Describe what action an entity can take." "Describe what action an entity can take."
), ),
conditions: SecretConditionV1Schema.describe( conditions: SecretConditionV1Schema.describe(