卡塞尔蒙哥
此软件包集成 卡塞尔 财务报告和审定财务报表 蒙戈DB,换句话说,它允许根据CASL规则从MongoDB获取记录并回答诸如"哪些记录可以读取"或"哪些记录可以更新"之类的问题.
安装
npm install @casl/mongoose @casl/ability
# or
yarn add @casl/mongoose @casl/ability
# or
pnpm add @casl/mongoose @casl/ability
使用量
@casl/mongoose 不仅可以与 鸽子 但与任何 蒙戈DB 联合来文司机 accessibleBy 帮助函数 .
accessibleBy 帮助者
这种整洁的帮助器功能可以将能力规则转换为MongoDB查询,只从数据库取取取可访问的记录. 它可以用来与绵羊或 MongoDB 适配器数字 :
MongoDB 适配器
const { accessibleBy } = require('@casl/mongoose');
const { MongoClient } = require('mongodb');
const ability = require('./ability');
async function main() {
const db = await MongoClient.connect('mongodb://localhost:27017/blog');
let posts;
try {
posts = await db.collection('posts').find(accessibleBy(ability, 'update').ofType('Post'));
} finally {
db.close();
}
console.log(posts);
}
这也可以与其他条件相结合,帮助: $and 运算符 :
posts = await db.collection('posts').find({
$and: [
accessibleBy(ability, 'update').ofType('Post'),
{ public: true }
]
});
重要的东西!:永远不要使用散开运算符(即, ...将下列条件结合起来: accessibleBy 由于您可能无意中覆盖了限制访问特定记录的属性:
// returns { authorId: 1 }
const permissionRestrictedConditions = accessibleBy(ability, 'update').ofType('Post');
// DANGER DO NOT DO THIS (see above use $and)
const query = {
// This is bad and potentially wrong code
...permissionRestrictedConditions,
authorId: 2
};
上面的情况,我们翻唱 authorId 基本上允许未经许可进入作者的 id = 2
如果没有定义特定动作/主题的权限, accessibleBy 将返回 { $expr: { $eq: [0, 1] } } 当它被发送到MongoDB时,数据库会返回一个空结果集.
乌鸦
const Post = require('./Post') // mongoose model
const ability = require('./ability') // defines Ability instance
async function main() {
const accessiblePosts = await Post.find(accessibleBy(ability).ofType('Post'));
console.log(accessiblePosts);
}
历史上, @casl/mongoose 本来是为了实现与 鸽子 但是现在我们重新调整它的方向 是更多的蒙戈DB 特定的包,因为复杂的工作 与mongoose类型 在TS.
可访问的记录插件
此插件已贬值, 建议使用的方法 accessibleBy 帮助函数。 。 。 .
accessibleRecordsPlugin 是一个添加的插件 accessibleBy (a) 探询方法以及梦游模型的静态方法。 我们可以在全球添加这个插件 :
const { accessibleRecordsPlugin } = require('@casl/mongoose');
const mongoose = require('mongoose');
mongoose.plugin(accessibleRecordsPlugin);
在调用前确保添加插件
mongoose.model(...)方法。 Mongoose 不会在调用前创建的模型中添加全局插件mongoose.plugin()。 。 。 .
或特定型号:
const mongoose = require('mongoose')
const { accessibleRecordsPlugin } = require('@casl/mongoose')
const Post = new mongoose.Schema({
title: String,
author: String
})
Post.plugin(accessibleRecordsPlugin)
module.exports = mongoose.model('Post', Post)
之后,我们可以使用 accessibleBy 方法打开 Post数字 :
const Post = require('./Post')
const ability = require('./ability') // defines Ability instance
async function main() {
const accessiblePosts = await Post.accessibleBy(ability);
console.log(accessiblePosts);
}
见 CASL 指南 学会如何定义能力
或在现有查询实例中:
const Post = require('./Post');
const ability = require('./ability');
async function main() {
const accessiblePosts = await Post.find({ status: 'draft' })
.accessibleBy(ability)
.select('title');
console.log(accessiblePosts);
}
accessibleBy 返回实例 mongoose.Query 也就是说你可以用任何 mongoose.Query方法(例如:, select, (中文(简体) ), limit, (中文(简体) ), sort) (中文(简体) ). 默认, accessibleBy 基于规则列表构建查询 read 动作,但我们可以通过提供第二个可选参数来改变这一点:
const Post = require('./Post');
const ability = require('./ability');
async function main() {
const postsThatCanBeUpdated = await Post.accessibleBy(ability, 'update');
console.log(postsThatCanBeUpdated);
}
accessibleBy建在顶部rulesToQuery函数从@casl/ability/extra读取 数据库查询能力 来了解它是如何运作的.
如果用户没有权限做特定动作, CASL 会丢弃 ForbiddenError 并且不会向MongoDB发送请求。 它还说 __forbiddenByCasl__: 1 附加安全条件.
例如,让我们找到所有用户可以删除的帖子(我们还没有定义删除的能力):
const { defineAbility } = require('@casl/ability');
const mongoose = require('mongoose');
const Post = require('./Post');
mongoose.set('debug', true);
const ability = defineAbility(can => can('read', 'Post', { private: false }));
async function main() {
try {
const posts = await Post.accessibleBy(ability, 'delete');
} catch (error) {
console.log(error) // ForbiddenError;
}
}
我们还可以利用由此产生的条件: 管道合计数字 :
const Post = require('./Post');
const ability = require('./ability');
async function main() {
const query = Post.accessibleBy(ability)
.where({ status: 'draft' })
.getQuery();
const result = await Post.aggregate([
{
$match: {
$and: [
query,
// other aggregate conditions
]
}
},
// other pipelines here
]);
console.log(result);
}
或进入 映射图数字 :
const Post = require('./Post');
const ability = require('./ability');
async function main() {
const query = Post.accessibleBy(ability)
.where({ status: 'draft' })
.getQuery();
const result = await Post.mapReduce({
query: {
$and: [
query,
// other conditions
]
},
map: () => emit(this.title, 1);
reduce: (_, items) => items.length;
});
console.log(result);
}
可访问字段By
accessibleFieldsBy 允许为主题类型或具体主题实例检索可用的字段 :
import { accessibleFieldsBy } from '@casl/mongoose';
import { Post } from './models';
accessibleFieldsBy(ability).ofType('Post') // returns accessible fields for Post model
accessibleFieldsBy(ability).ofType(Post) // also possible to pass class if classes are used for rule definition
accessibleFieldsBy(ability).of(new Post()) // returns accessible fields for Post model
这个帮助器可以在我们只需要发送一个模型中可访问的部分响应时直接使用:
const pick = require('lodash/pick');
const { accessibleFieldsBy } = require('@casl/mongoose');
const Post = require('./Post');
app.get('/api/posts/:id', async (req, res) => {
const post = await Post.accessibleBy(ability).findByPk(req.params.id);
res.send(pick(post, accessibleFieldsBy(ability).of(post)));
});
当用模型类型调用时, accessibleFieldsBy 不考虑实例条件。 也一样 检查逻辑 作为 Ability是因为 can 方法。 让我们举个例子来回顾一下:
const { defineAbility } = require('@casl/ability');
const Post = require('./Post');
const ability = defineAbility((can) => {
can('read', 'Post', ['title'], { private: true });
can('read', 'Post', ['title', 'description'], { private: false });
});
const post = new Post({ private: true, title: 'Private post' });
accessibleFieldsBy(ability).ofType(Post); // ['title', 'description']
accessibleFieldsBy(ability).of(post); // ['title']
如您所见, 模式级访问返回所有可以读取的字段 。 同时,可读取此特性的例级访问返回字段 post 举个例子 所以除了减少应用软件和数据库之间的流量之外 通常没什么意义 把模型级访问的结果传入 mongoose.Query是因为 select 方法 因为你最终需要调用 accessibleFieldsBy 在每一个例子.
此助手已预配置以获取全部字段 Model.schema.paths。如果不需要,请定义您的自定义助手 :
import { AnyMongoAbility, Generics } from "@casl/ability";
import { AccessibleFields, GetSubjectTypeAllFieldsExtractor } from "@casl/ability/extra";
import mongoose from 'mongoose';
const getSubjectTypeAllFieldsExtractor: GetSubjectTypeAllFieldsExtractor = (type) => {
/** custom implementation of returning all fields */
};
export function accessibleFieldsBy<T extends AnyMongoAbility>(
ability: T,
action: Parameters<T['rulesFor']>[0] = 'read'
): AccessibleFields<Extract<Generics<T>['abilities'], unknown[]>[1]> {
return new AccessibleFields(ability, action, getSubjectTypeAllFieldsExtractor);
}
在mongoose中支持脚本类型
软件包被用TypeScript写出,这使得与插件和帮助器一起工作更容易,因为IDE提供了有用的提示. 让我们看看它的行动!
如果我们有 Post 可称为:
import mongoose from 'mongoose';
export interface Post extends mongoose.Document {
title: string
content: string
published: boolean
}
const PostSchema = new mongoose.Schema<Post>({
title: String,
content: String,
published: Boolean
});
export const Post = mongoose.model('Post', PostSchema);
延长 Post 模式为 accessibleBy 方法足以包含相应的插件(全球或本地在 Post)和相应的使用 Model 类型。 所以,让我们改变这个例子,所以它包括 accessibleRecordsPlugin数字 :
import { accessibleRecordsPlugin, AccessibleRecordModel } from '@casl/mongoose';
// all previous code, except last line
PostSchema.plugin(accessibleRecordsPlugin);
export const Post = mongoose.model<Post, AccessibleRecordModel<Post>>('Post', PostSchema);
// Now we can safely use `Post.accessibleBy` method.
Post.accessibleBy(/* parameters */)
Post.where(/* parameters */).accessibleBy(/* parameters */);
accessibleFieldsBy 不需要针对老鼠的种类。 您可以直接使用模型或文档:
import { accessibleFieldsBy } from '@casl/mongoose';
accessibleFieldsBy(ability).ofType(Post);
const post = new Post();
accessibleFieldsBy(ability).of(post);
如果您想要类似已删除插件的模型或文档实例方法, 请在周围创建一个小型的 app级包装 accessibleFieldsBy 这符合你自己的打字和现场曝光规则.
想帮忙吗?
想要提交错误, 提供一些代码, 还是改进文档 ? 不错! 阅读指南 捐款。 。 。 .
如果你想帮助我们 维持我们的社区和项目,考虑 成为开放集体的出资人
见 支助CASL 详情