卡塞尔·普里斯马
此软件包允许定义 卡塞尔 权限在 普里斯马 使用 Prisma 的模型 WhereInput. 这在SQL世界的许可管理方面带来了很大的力量:
- 我们可以使用 Prisma 查询来定义权限, 无需再学习 MongoDB 查询语言 .
- 此外,我们可以问我们的SQL数据库的问题,例如:"哪些记录可以读取"或者"哪些记录可以更新".
安装
npm install @casl/prisma @casl/ability
# or
yarn add @casl/prisma @casl/ability
# or
pnpm add @casl/prisma @casl/ability
@casl/prisma 需要 Prisma 客户端 4.1.6.0 或稍后。 Prisma 客户端扩展为 createCaslExtension 需要添加到 Prisma 客户端的帮助 !
使用量
这个软件包与其它软件包有些不同,因为它提供了一个自定义 createPrismaAbility 配置用于使用 Prisma 检查权限的工厂函数 输入位置数字 :
import { User, Post, Prisma } from '@prisma/client';
import { Ability, AbilityBuilder, subject, type ForcedSubject, type TaggedSubject } from '@casl/ability';
import { createPrismaAbility, PrismaQuery } from '@casl/prisma';
type AppSubjects =
| User & ForcedSubject<'User'>
| Post & ForcedSubject<'Post'>
type AppAbility = Ability<[string, AppSubjects], PrismaQuery, TaggedSubject>;
const { can, cannot, build } = new AbilityBuilder<AppAbility>(createPrismaAbility);
can('read', 'Post', { authorId: 1 });
cannot('read', 'Post', { title: { startsWith: '[WIP]:' } });
const ability = build();
ability.can('read', 'Post');
ability.can('read', subject('Post', { title: '...', authorId: 1 })));
见 CASL 指南 学习如何定义能力。 除条件语言外,一切都是一样的.
如果您使用 Prisma 7 生成 Prisma 客户端
prisma-client生成器, 从您生成的客户端路径导入模型类型( 例如 )./prisma/generated/client并创建一个小包装@casl/prisma/runtime如下所示.
关于主题助手的说明
ForcedSubject<'Post'> 在对象上声明标记 。 当主题结合只包含标记对象类型时,必须指定 TaggedSubject 作为第三个通用参数: Ability 或者说 PrismaAbility 还接受主题名称,例如: 'Post' 在许可检查中:
type AppAbility = Ability<[string, AppSubjects], PrismaQuery, TaggedSubject>;
TaggedSubject 用途 __caslSubjectType__ 在默认情况下,匹配 subject 帮手 如果你忽略了第三个通用参数, DefaultSubject 保留已存在的行为: 明确包含名称和标记对象类型, 例如 Ability<[string, 'User' | 'Post' | AppSubjects], PrismaQuery>。 。 。 .
因为Prisma在不披露任何类型信息的情况下返回了DTO对象,所以我们需要使用 subject 帮助手动提供该类型,因此CASL可以理解哪些规则适用于对象所传.
不幸的是,除了在所有模型中增加一栏之外,没有实现自动化的简单方法。 详情请检查 这个问题。 。 。 .
要了解更多关于对象类型的检测细节, 请读 CASL 主题类型检测
关于 Prisma 查询运行时解释器的说明
@casl/prisma 用途 播音员 解释 Prisma 输入位置 在 JavaScript 运行时。 然而,没有多少说明:
- 未实现 JSON 列的平等
- 未实现数组/列表列的等同性(尽管操作符喜欢)
has, (中文(简体) ),hasSome财务报告和审定财务报表hasEvery足够多了) - 在确定关系条件时,总是指定一个操作员(
every, (中文(简体) ),none, (中文(简体) ),some, (中文(简体) ),is或者说isNot(中文(简体) )
口译员投出a ParsingQueryError 如果它接收到查询操作器的无效参数,或者如果某些操作没有被支持.
Prisma 7 配置
Prisma 7 将连接细节移动到 prisma.config.ts 不再装入 .env 文件自动。 在您的方案上添加配置 prisma generate 继续工作 :
// prisma.config.ts
import 'dotenv/config';
import { defineConfig } from 'prisma/config';
export default defineConfig({
schema: './schema.prisma',
datasource: {
// fall back to a local URL so generate does not fail in CI
url: process.env.DATABASE_URL ?? 'postgresql://postgres:postgres@localhost:5432/postgres',
},
});
如果你坚持遗产 prisma-client-js 生成器, 默认 @prisma/client 导入继续工作.
查找可访问的记录
一个不错的特征 普里斯马 财务报告和审定财务报表 卡塞尔 整合就是我们能够从数据库中获取所有记录,我们的用户可以访问. 要做到这一点,只是使用 accessibleBy 帮助函数 :
// ability is a PrismaAbility instance created in the example above
const accessiblePosts = await prisma.post.findMany({
where: accessibleBy(ability).ofType('Post')
});
该函数接受 Ability 实例和实例 action (违约) read),返回实例 AccessibleRecords 带有 ofType 用于接受 Prisma 模型名称并返回从权限规则汇总的对象的方法 WhereInput。 。 。 .
重要:如果用户无法访问任何帖子, .ofType 返回一个特殊的空条件。 如果您直接传递给 Prisma 客户端, 无需 createCaslExtension,Prisma 拒绝查询,因为该条件包含一个内部标记字段. 这使得被拒绝的访问被关闭.
把这个与商业逻辑条件结合起来,只需要使用 AND数字 :
const accessiblePosts = await prisma.post.findMany({
where: {
AND: [
accessibleBy(ability).ofType('Post'),
{ /* business related conditions */ }
]
}
})
为当前Prisma扩展执行,设置 accessibleBy(...) 在复合条件下,例如 AND: [...] 允许更快地对空条件检测到短路.
Prisma 客户端扩展名
createCaslExtension 地址 这个问题 通过让Prisma客户端将 CASL 的空条件作为空查询结果处理,而不是丢弃被禁错误.
重要内容: 请确保将此扩展添加到您用于 CASL 保护查询的 Prisma 客户端实例中。 否则,某些CASL限制可能应用不当.
添加为其它常规扩展 :
import { PrismaClient } from '@prisma/client';
import { createCaslExtension } from '@casl/prisma';
const prisma = new PrismaClient().$extends(createCaslExtension());
有了这个扩展,带有空 CASL 条件的操作返回同样类型的"无记录"结果 Prisma 用户通常期望:
const posts = await prisma.post.findMany({
where: accessibleBy(ability).ofType('Post')
});
// posts === []
需要匹配记录的单一记录操作,例如 findFirstOrThrow, (中文(简体) ), findUniqueOrThrow, (中文(简体) ), update,以及 delete,将会以“没有找到...”错误的记录来拒绝.
类型脚本支持
软件包是用TypeScript编写的,它提供了全面的IDE提示并编译了时间验证.
一定要打电话
prisma generate。 。 。 .@casl/prisma使用 Prisma 生成的类型, 因此如果客户端没有生成, 将没有任何效果 .
此外,还有几个帮助者使与Prisma和CASL合作变得容易:
Prisma 查询
它是一种通用类型,提供 Prisma.ModelWhereInput 以一般方式。 我们需要通过 一个命名的模型:
import type { User } from '@prisma/client';
import type { ForcedSubject } from '@casl/ability';
import type { PrismaQuery } from '@casl/prisma';
// almost the same as Prisma.UserWhereInput except that it's a higher order type
type UserWhereInput = PrismaQuery<User & ForcedSubject<'User'>>;
自定义 Prisma 客户端输出路径( Prisma 7 默认)
普丽斯玛7's prisma-client 生成器将客户端写入自定义目录, 所以 @prisma/client 不再再出口您的项目类型。 将 CASL 点在生成的客户端上, 并使用一个建在上的小包装 @casl/prisma/runtime数字 :
// schema.prisma
generator client {
provider = "prisma-client"
output = "../src/generated/client"
}
// prisma.config.ts
import { defineConfig } from 'prisma/config';
export default defineConfig({
schema: './schema.prisma',
datasource: { url: process.env.DATABASE_URL! },
});
// src/casl-prisma.ts
import {
accessibleBy,
createPrismaAbility,
ParsingQueryError,
prismaQuery,
type PrismaQueryOf,
type WhereInputOf,
} from '@casl/prisma/runtime';
import type { Ability, ForcedSubject, TaggedSubject } from "@casl/ability";
import type { Prisma, Post, User } from './generated/client';
export { accessibleBy, createPrismaAbility, ParsingQueryError, prismaQuery };
export type PrismaQuery<T extends ForcedSubject<any> = ForcedSubject<any>> =
PrismaQueryOf<Prisma.TypeMap, T>;
export type WhereInput<TModelName extends Prisma.ModelName> =
WhereInputOf<Prisma.TypeMap, TModelName>;
export type AppSubjects =
| User & ForcedSubject<'User'>
| Post & ForcedSubject<'Post'>
;
export type AppAbility = Ability<['create' | "read" | "update" | "delete", 'all' | AppSubjects], PrismaQuery, TaggedSubject>
在您的应用程序中使用此包装而非导入 @casl/prisma 直接说 如果你坚持遗产 prisma-client-js prisma 6.x 兼容性生成器, 默认值 @casl/prisma 进入点继续工作.
想帮忙吗?
想要提交错误, 提供一些代码, 还是改进文档 ? 不错! 阅读指南 捐款。 。 。 .
如果你想帮助我们 维持我们的社区和项目,考虑 成为开放集体的出资人
见 支助CASL 详情