Skip to content

Commit

Permalink
fix: update to prisma v5.11.0
Browse files Browse the repository at this point in the history
  • Loading branch information
LucianBuzzo committed Apr 9, 2024
1 parent 3cd9dc7 commit c4ffd22
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 129 deletions.
37 changes: 10 additions & 27 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

94 changes: 45 additions & 49 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,51 +1,47 @@
{
"name": "@cerebruminc/yates",
"version": "3.5.1",
"description": "Role based access control for Prisma Apps",
"main": "dist/index.js",
"files": [
"CHANGELOG.md",
"dist",
"images"
],
"scripts": {
"generate": "prisma generate",
"build": "rimraf dist && tsc -p tsconfig.build.json",
"test": "npm run lint",
"lint": "biome check .",
"lint:fix": "biome check . --apply",
"test:types": "tsc --noEmit",
"test:integration": "jest --runInBand test/integration",
"test:compose:integration": "docker compose -f docker-compose.yml --profile with-sut up db sut --exit-code-from sut",
"setup": "prisma generate && prisma migrate dev",
"prepublishOnly": "npm run build"
},
"author": "Cerebrum <[email protected]> (https://cerebrum.com)",
"license": "MIT",
"devDependencies": {
"@biomejs/biome": "1.5.3",
"@prisma/client": "^5.0.0",
"@types/cls-hooked": "^4.3.3",
"@types/jest": "^29.2.6",
"@types/lodash": "^4.14.191",
"@types/uuid": "^9.0.0",
"cls-hooked": "^4.2.2",
"jest": "^29.3.1",
"prisma": "^5.0.0",
"rimraf": "^5.0.5",
"ts-jest": "^29.0.5",
"typescript": "^5.3.3",
"uuid": "^9.0.0"
},
"dependencies": {
"@types/debug": "^4.1.12",
"debug": "^4.3.4",
"lodash": "^4.17.21",
"node-sql-parser": "^4.12.0",
"type-fest": "^4.10.3"
},
"peerDependencies": {
"@prisma/client": "^5.0.0",
"prisma": "^5.0.0"
}
"name": "@cerebruminc/yates",
"version": "3.5.1",
"description": "Role based access control for Prisma Apps",
"main": "dist/index.js",
"files": ["CHANGELOG.md", "dist", "images"],
"scripts": {
"generate": "prisma generate",
"build": "rimraf dist && tsc -p tsconfig.build.json",
"test": "npm run lint",
"lint": "biome check .",
"lint:fix": "biome check . --apply",
"test:types": "tsc --noEmit",
"test:integration": "jest --runInBand test/integration",
"test:compose:integration": "docker compose -f docker-compose.yml --profile with-sut up db sut --exit-code-from sut",
"setup": "prisma generate && prisma migrate dev",
"prepublishOnly": "npm run build"
},
"author": "Cerebrum <[email protected]> (https://cerebrum.com)",
"license": "MIT",
"devDependencies": {
"@biomejs/biome": "1.5.3",
"@prisma/client": "^5.11.0",
"@types/cls-hooked": "^4.3.3",
"@types/jest": "^29.2.6",
"@types/lodash": "^4.14.191",
"@types/uuid": "^9.0.0",
"cls-hooked": "^4.2.2",
"jest": "^29.3.1",
"prisma": "^5.0.0",
"rimraf": "^5.0.5",
"ts-jest": "^29.0.5",
"typescript": "^5.3.3",
"uuid": "^9.0.0"
},
"dependencies": {
"@types/debug": "^4.1.12",
"debug": "^4.3.4",
"lodash": "^4.17.21",
"node-sql-parser": "^4.12.0",
"type-fest": "^4.10.3"
},
"peerDependencies": {
"@prisma/client": "^5.11.0",
"prisma": "^5.11.0"
}
}
80 changes: 41 additions & 39 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,6 @@ export const createClient = (

const setRLS = async <ContextKeys extends string, YModel extends Models>(
prisma: PrismaClient,
existingAbilities: PgYatesAbility[],
table: string,
roleName: string,
slug: string,
Expand All @@ -324,50 +323,54 @@ const setRLS = async <ContextKeys extends string, YModel extends Models>(
throw new Error("Expression must be defined for RLS abilities");
}

// Check if RLS exists
const policyName = roleName;
const existingAbility = existingAbilities.find(
(row) =>
row.ability_model === table && row.ability_policy_name === policyName,
);

let shouldUpdateAbilityTable = false;

// IF RLS doesn't exist or expression is different, set RLS
if (!existingAbility) {
debug("Creating RLS policy for", roleName, "on", table, "for", operation);
const expression = await expressionToSQL(rawExpression, table);

// If the operation is an insert or update, we need to use a different syntax as the "WITH CHECK" expression is used.
if (operation === "INSERT") {
await prisma.$queryRawUnsafe(`
// Take a lock and run the RLS setup in a transaction to prevent conflicts
// in a multi-server environment
await prisma.$transaction(async (tx) => {
await takeLock(tx as PrismaClient);
// Check if RLS exists
const policyName = roleName;
const existingAbilities: PgYatesAbility[] = await tx.$queryRaw`
select * from _yates._yates_abilities where ability_model = ${table} and ability_policy_name = ${policyName}
`;
const existingAbility = existingAbilities[0];

let shouldUpdateAbilityTable = false;

// IF RLS doesn't exist or expression is different, set RLS
if (!existingAbility) {
debug("Creating RLS policy for", roleName, "on", table, "for", operation);
const expression = await expressionToSQL(rawExpression, table);

// If the operation is an insert or update, we need to use a different syntax as the "WITH CHECK" expression is used.
if (operation === "INSERT") {
await tx.$queryRawUnsafe(`
CREATE POLICY ${policyName} ON "public"."${table}" FOR ${operation} TO ${roleName} WITH CHECK (${expression});
`);
} else {
await prisma.$queryRawUnsafe(`
} else {
await tx.$queryRawUnsafe(`
CREATE POLICY ${policyName} ON "public"."${table}" FOR ${operation} TO ${roleName} USING (${expression});
`);
}
shouldUpdateAbilityTable = true;
} else if (existingAbility.ability_expression !== rawExpression.toString()) {
debug("Updating RLS policy for", roleName, "on", table, "for", operation);
const expression = await expressionToSQL(rawExpression, table);
if (operation === "INSERT") {
await prisma.$queryRawUnsafe(`
}
shouldUpdateAbilityTable = true;
} else if (
existingAbility.ability_expression !== rawExpression.toString()
) {
debug("Updating RLS policy for", roleName, "on", table, "for", operation);
const expression = await expressionToSQL(rawExpression, table);
if (operation === "INSERT") {
await tx.$queryRawUnsafe(`
ALTER POLICY ${policyName} ON "public"."${table}" TO ${roleName} WITH CHECK (${expression});
`);
} else {
await prisma.$queryRawUnsafe(`
} else {
await tx.$queryRawUnsafe(`
ALTER POLICY ${policyName} ON "public"."${table}" TO ${roleName} USING (${expression});
`);
}
shouldUpdateAbilityTable = true;
}
shouldUpdateAbilityTable = true;
}

if (shouldUpdateAbilityTable) {
await prisma.$transaction([
takeLock(prisma),
upsertAbility(prisma, {
if (shouldUpdateAbilityTable) {
await upsertAbility(tx as PrismaClient, {
ability_model: table,
ability_name: slug,
ability_policy_name: policyName,
Expand All @@ -376,9 +379,9 @@ const setRLS = async <ContextKeys extends string, YModel extends Models>(
// We store the string representation of the expression so that
// we can compare it later without having to recompute the SQL
ability_expression: rawExpression.toString(),
}),
]);
}
});
}
});
};

export const createRoles = async <
Expand Down Expand Up @@ -559,7 +562,6 @@ export const createRoles = async <
if (ability.expression) {
await setRLS(
prisma,
existingAbilities,
table,
roleName,
slug,
Expand Down
5 changes: 3 additions & 2 deletions test/integration/abilities.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe("abilities", () => {
operation: "SELECT",
expression: (_client, _row, _context) => {
return {
email: mail,
name: "John Doe",
};
},
},
Expand All @@ -48,7 +48,7 @@ describe("abilities", () => {
operation: "INSERT",
expression: (_client, _row, _context) => {
return {
email: mail,
name: "John Doe",
};
},
},
Expand All @@ -75,6 +75,7 @@ describe("abilities", () => {

const user = await client.user.create({
data: {
name: "John Doe",
email: mail,
},
});
Expand Down
5 changes: 3 additions & 2 deletions test/integration/expressions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,6 @@ describe("expressions", () => {
expression: (client: PrismaClient, row) => {
return client.item.findFirst({
where: {
id: item.id,
value: row("id"),
},
});
Expand Down Expand Up @@ -715,7 +714,7 @@ describe("expressions", () => {
client.post.findFirstOrThrow({ where: { id: post.id } }),
).rejects.toThrow();

await adminClient.item.update({
const udpate = await adminClient.item.update({
where: {
id: item.id,
},
Expand All @@ -726,6 +725,8 @@ describe("expressions", () => {
},
});

console.log("udpate", udpate);

const foundPost = await client.post.findFirstOrThrow({
where: { id: post.id },
});
Expand Down
12 changes: 7 additions & 5 deletions test/integration/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,11 +341,13 @@ describe("setup", () => {
},
});

await client.post.delete({
where: {
id: postId2,
},
});
await expect(
client.post.delete({
where: {
id: postId2,
},
}),
).rejects.toThrow("Record to delete does not exist");

const exists2 = await adminClient.post.findUnique({
where: {
Expand Down
4 changes: 2 additions & 2 deletions test/integration/migrations.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ describe("migrations", () => {
prisma: initial,
customAbilities: {
Post: {
readWithTitle: {
readWithTitle_fa0xC: {
description: "Read posts with a special title",
operation: "SELECT",
expression: "title = 'Special title'",
Expand All @@ -125,7 +125,7 @@ describe("migrations", () => {
},
getRoles(abilities) {
return {
[role]: [abilities.Post.readWithTitle],
[role]: [abilities.Post.readWithTitle_fa0xC],
};
},
getContext: () => ({
Expand Down
Loading

0 comments on commit c4ffd22

Please sign in to comment.